﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Threading;
using System.Reflection;
using System.Collections;
using King.Text;
using SysDateTime = System.DateTime;

using King.Extensions;
using King.Reflection;
using King.Collections;
using King.Loader;
using King.Xml.Reflection;
using King.Xml.Reflection.Emit;
using King.Xml;
using System.Dynamic;

namespace King.Xml.Reflection {
    public sealed class XsSerializationException : Exception {
        public XsSerializationException() { }
        public XsSerializationException(string message) : base(message) { }
    }

    public sealed class XsLoaderException : Exception {
        public XsLoaderException(string message) : base(message) { }
    }

    public static class Extensions {
        public static XDocument ToXsd(this XsSchema xsSchema) {
            return XsXDocumentFactory.ToXsd(xsSchema);
        }
        internal static XObject ToXsd(this XsType xsType) {
            return XsXDocumentFactory.ToXsd(xsType);
        }
        internal static XObject ToXsd(this XsElement xsElement) {
            return XsXDocumentFactory.ToXsd(xsElement);
        }
    }

    public enum XsWhitespaceFacetType {
        Preserve = 1,
        Replace,
        Collapse,
    }
    public enum XsExplicitTimezoneFacetType {
        Optional = 1,
        Required,
        Prohibited,
    }
    [Flags]
    public enum XsFacetType {
        None = 0,

        // cardinality
        Cardinality = 1 << 0,
        MaximumCardinality = 1 << 1,
        MinimumCardinality = 1 << 2,

        // units
        Length = 1 << 3,
        MinimumLength = 1 << 4,
        MaximumLength = 1 << 5,

        // lexical
        Pattern = 1 << 6,
        Whitespace = 1 << 7,

        // value
        MaximumInclusive = 1 << 8,
        MaximumExclusive = 1 << 9,
        MinimumInclusive = 1 << 10,
        MinimumExclusive = 1 << 11,

        // base-10
        TotalDigits = 1 << 12,
        FractionDigits = 1 << 13,

        Enumeration = 1 << 14,
        ExplicitTimeZone = 1 << 15,
        Assertions = 1 << 16,
    }
    [Flags]
    public enum XsNamespaceType {
        None            = 0,
        Any             = 1 << 1,
        Other           = 1 << 2,
        Local           = 1 << 3,
        TargetNamespace = 1 << 4,
    }
    public enum XsProcessContents {
        Undefined = 0,
        Strict,
        Lax,
        Skip,
    }
    public enum XsAttributeUse {
        Optional,
        Prohibitied,
        Required
    }
    public enum XsForm {
        Undefined,
        Unqualified,
        Qualified,
    }
    [Flags]
    public enum XsFinal {
        None = 0,
        Extension = 1 << 0,
        Restriction = 1 << 2,
        List = 1 << 3,
        Union = 1 << 4,
        All = Extension | Restriction | List | Union,
    }
    public enum XsOrdered {
        False,
        Total,
        Partial
    }
    public enum XsCardinality {
        Finite,
        CountablyInfinite
    }

    public struct XsDateTime {
        public static bool operator !=(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs != (DateTime)rhs;
            return (DateTimeOffset)lhs != (DateTimeOffset)rhs;
        }
        public static bool operator <(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs < (DateTime)rhs;
            return (DateTimeOffset)lhs < (DateTimeOffset)rhs;
        }
        public static bool operator <=(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs <= (DateTime)rhs;
            return (DateTimeOffset)lhs <= (DateTimeOffset)rhs;
        }
        public static bool operator ==(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs == (DateTime)rhs;
            return (DateTimeOffset)lhs == (DateTimeOffset)rhs;
        }
        public static bool operator >(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs > (DateTime)rhs;
            return (DateTimeOffset)lhs > (DateTimeOffset)rhs;
        }
        public static bool operator >=(XsDateTime lhs, XsDateTime rhs) {
            if (lhs.IsDateTime)
                return (DateTime)lhs >= (DateTime)rhs;
            return (DateTimeOffset)lhs >= (DateTimeOffset)rhs;
        }

        public static explicit operator DateTime(XsDateTime union) {
            return union.DateTime;
        }
        public static explicit operator DateTimeOffset(XsDateTime union) {
            return union.DateTimeOffset;
        }
        public static implicit operator XsDateTime(DateTime dateTime) {
            return new XsDateTime(dateTime);
        }
        public static implicit operator XsDateTime(DateTimeOffset dateTimeOffset) {
            return new XsDateTime(dateTimeOffset);
        }

        private readonly object m_dateTimeOrDateTimeOffset;

        internal XsDateTime(DateTime dateTime) {
            m_dateTimeOrDateTimeOffset = dateTime;
        }
        internal XsDateTime(DateTimeOffset dateTimeOffset) {
            m_dateTimeOrDateTimeOffset = dateTimeOffset;
        }

        public DateTime DateTime {
            get { return (DateTime)m_dateTimeOrDateTimeOffset; }
        }
        public DateTimeOffset DateTimeOffset {
            get { return (DateTimeOffset)m_dateTimeOrDateTimeOffset; }
        }
        public bool IsDateTime {
            get { return m_dateTimeOrDateTimeOffset is DateTime; }
        }
        public bool IsDateTimeOffset {
            get { return m_dateTimeOrDateTimeOffset is DateTimeOffset; }
        }

        public override bool Equals(object obj) {
            if (obj is XsDateTime)
                return (XsDateTime)obj == this;

            if (obj is DateTime && IsDateTime)
                return DateTime == (DateTime)obj;

            if (obj is DateTimeOffset && IsDateTimeOffset)
                return DateTimeOffset == (DateTimeOffset)obj;

            return false;
        }
        public override int GetHashCode() {
            return m_dateTimeOrDateTimeOffset.GetHashCode();
        }

        public override string ToString() {
            return m_dateTimeOrDateTimeOffset.ToString();
        }
    }

    public sealed partial class XsLoader {

        #region XsdNames
        private static readonly string[] XsdNames = new[] {
            "anyType",
            "anySimpleType",
            "anyAtomicType",
            "string",
            "boolean",
            "decimal",
            "float",
            "double",
            "base64Binary",
            "hexBinary",
            "anyUri",
            "qName",
            "duration",
            "date",
            "time",
            "dateTime",
            "gYear",
            "gMonth",
            "gDay",
            "gYearMonth",
            "gMonthDay",
            "integer",
            "long",
            "int",
            "short",
            "byte",
            "nonNegativeInteger",
            "positiveInteger",
            "unsignedLong",
            "unsignedInt",
            "unsignedShort",
            "unsignedByte",
            "nonPositiveInteger",
            "negativeInteger",
            "dayTimeDuration",
            "yearMonthDuration",
            "dateTimeStamp",
        };
        #endregion

        public const string XsdNamespace = "http://www.w3.org/2001/XMLSchema";
        public const string XsdDefaultPrefix = "xs";
        public static readonly XPrefix XsdDefaultXPrefix = (XPrefix)XsdDefaultPrefix;
        public static readonly XNamespace XsdXNamespace = (XNamespace)XsdNamespace;

        private readonly Loader<XNamespace, XsSchema> m_schemaLoader;
        private readonly Lazy<XsSchema> m_xsdSchema;

        public XsLoader(
            Func<XsSchemaFactory, XsSchema> schemaFactory = null) {

            m_schemaLoader = new Loader<XNamespace, XsSchema>(
                keys: o => Enumerable.Empty<object>(),
                defaultResolver: o => {

                    var factory = new XsSchemaFactory(this, o.Key);
                    if (factory.XNamespace == XsdXNamespace) {
                        return factory.CreateSchema(
                            xNamespaceByXPrefix: 
                                new Lazy<Dictionary<XPrefix, XNamespace>>(() => {
                                    var result = new Dictionary<XPrefix, XNamespace>();
                                    result[XsdDefaultXPrefix] = XsdXNamespace;
                                    return result;
                                }),
                            typeNames: x => XsdNames,
                            typeFactory: CreateXsdType
                        );
                    }

                    if (schemaFactory != null)
                        return schemaFactory(factory);

                    return null;
                },
                getLoader: o => o.Loader.m_schemaLoader, 
                getKeys: o => new[] { 
                    o.XNamespace 
                },
                normalizeKey: o => (XNamespace)o
            );
            
            m_xsdSchema = new Lazy<XsSchema>(() => GetSchema(XsdXNamespace));
        }

        private XsType CreateXsdType(XsTypeFactory factory) {

            var schema = factory.Schema;

            switch (factory.XName.LocalName) {

                case "anyType": return new XsFundamentalType.AnyType(schema);
                case "anySimpleType": return new XsFundamentalType.AnySimpleType(schema);
                case "anyAtomicType": return new XsFundamentalType.AnyAtomicType(schema);
                case "string": return new XsFundamentalType.String(schema);
                case "boolean": return new XsFundamentalType.Boolean(schema);
                case "decimal": return new XsFundamentalType.Decimal(schema);
                case "float": return new XsFundamentalType.Float(schema);
                case "double": return new XsFundamentalType.Double(schema);
                case "base64Binary": return new XsFundamentalType.Base64Binary(schema);
                case "hexBinary": return new XsFundamentalType.HexBinary(schema);
                case "anyUri": return new XsFundamentalType.AnyUri(schema);
                case "qName": return new XsFundamentalType.QualifiedName(schema);
                case "duration": return new XsFundamentalType.Duration(schema);
                case "date": return new XsFundamentalType.Date(schema);
                case "time": return new XsFundamentalType.Time(schema);
                case "dateTime": return new XsFundamentalType.DateTime(schema);
                case "gYear": return new XsFundamentalType.Year(schema);
                case "gMonth": return new XsFundamentalType.Month(schema);
                case "gDay": return new XsFundamentalType.Day(schema);
                case "gYearMonth": return new XsFundamentalType.YearMonth(schema);
                case "gMonthDay": return new XsFundamentalType.MonthDay(schema);
                        
                case "integer":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Decimal, 
                        facets: o => new XsFacet[] {
                            o.CreateFractionDigitsFacet((decimal)0, isFixed: true),
                            o.CreatePatternFacet(@"([-]|[+])?[0-9]+"),
                        }
                    );

                case "long":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Integer, 
                        clrType: typeof(long),
                        parseLiteral: (o, t) => {
                            if (t)
                                long.Parse(o);
                            long result;
                            if (!long.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(long.MaxValue),
                            o.CreateMinimumInclusiveFacet(long.MinValue)
                        }
                    );

                case "int":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Long,
                        clrType: typeof(int),
                        parseLiteral: (o, t) => {
                            if (t)
                                int.Parse(o);
                            int result;
                            if (!int.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(int.MaxValue),
                            o.CreateMinimumInclusiveFacet(int.MinValue)
                        }
                    );

                case "short":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Int,
                        clrType: typeof(short),
                        parseLiteral: (o, t) => {
                            if (t)
                                short.Parse(o);
                            short result;
                            if (!short.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(short.MaxValue),
                            o.CreateMinimumInclusiveFacet(short.MinValue)
                        }
                    );

                case "byte":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Short,
                        clrType: typeof(sbyte),
                        parseLiteral: (o, t) => {
                            if (t)
                                sbyte.Parse(o);
                            sbyte result;
                            if (!sbyte.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(sbyte.MaxValue),
                            o.CreateMinimumInclusiveFacet(sbyte.MinValue)
                        }
                    );

                case "nonNegativeInteger":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Integer,
                        facets: o => new XsFacet[] {
                            o.CreateMinimumInclusiveFacet((decimal)0)
                        }
                    );

                case "positiveInteger":
                    return factory.DefineBuiltInSimpleType(
                        baseType: NonNegativeInteger,
                        facets: o => new XsFacet[] {
                            o.CreateMinimumInclusiveFacet((decimal)1)
                        }
                    );

                case "unsignedLong":
                    return factory.DefineBuiltInSimpleType(
                        baseType: NonNegativeInteger,
                        clrType: typeof(ulong),
                        parseLiteral: (o, t) => {
                            if (t)
                                ulong.Parse(o);
                            ulong result;
                            if (!ulong.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(ulong.MaxValue),
                            o.CreateMinimumInclusiveFacet(ulong.MinValue)
                        }
                    );

                case "unsignedInt":
                    return factory.DefineBuiltInSimpleType(
                        baseType: UnsignedLong,
                        clrType: typeof(uint),
                        parseLiteral: (o, t) => {
                            if (t)
                                uint.Parse(o);
                            uint result;
                            if (!uint.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(uint.MaxValue),
                            o.CreateMinimumInclusiveFacet(uint.MinValue)
                        }
                    );

                case "unsignedShort":
                    return factory.DefineBuiltInSimpleType(
                        baseType: UnsignedInt,
                        clrType: typeof(ushort),
                        parseLiteral: (o, t) => {
                            if (t)
                                ushort.Parse(o);
                            ushort result;
                            if (!ushort.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(ushort.MaxValue),
                            o.CreateMinimumInclusiveFacet(ushort.MinValue)
                        }
                    );

                case "unsignedByte":
                    return factory.DefineBuiltInSimpleType(
                        baseType: UnsignedShort,
                        clrType: typeof(byte),
                        parseLiteral: (o, t) => { 
                            if (t)
                                byte.Parse(o); 
                            byte result;
                            if (!byte.TryParse(o, out result))
                                return null;
                            return result;
                        },
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet(byte.MaxValue),
                            o.CreateMinimumInclusiveFacet(byte.MinValue)
                        }
                    );

                case "nonPositiveInteger":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Integer,
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet((decimal)0),
                        }
                    );

                case "negativeInteger":
                    return factory.DefineBuiltInSimpleType(
                        baseType: NonPositiveInteger,
                        facets: o => new XsFacet[] {
                            o.CreateMaximumInclusiveFacet((decimal)-1),
                        }
                    );

                case "dayTimeDuration":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Duration,
                        facets: o => new XsFacet[] {
                            //todo
                        }
                    );

                case "yearMonthDuration":
                    return factory.DefineBuiltInSimpleType(
                        baseType: Duration,
                        facets: o => new XsFacet[] {
                            //todo
                        }
                    );

                case "dateTimeStamp":
                    return factory.DefineBuiltInSimpleType(
                        baseType: DateTime,
                        facets: o => new XsFacet[] {
                            o.CreateExplicitTimeZoneFacet(XsExplicitTimezoneFacetType.Required)
                        }
                    );

                default:
                    return null;
            }
        }

        public XsSchema Xsd {
            get { return m_xsdSchema.Value; }
        }
        public event Func<XsSchemaFactory, XsSchema> SchemaResolve {
            add { m_schemaLoader.Resolve += o => value(new XsSchemaFactory(this, o.Key)); }
            remove { throw new InvalidOperationException(); }
        }

        public XsSchema GetSchema(XNamespace xNamespace) {
            return m_schemaLoader.TryGet(xNamespace);
        }
        public IEnumerable<XsSchema> Schemas() {
            return m_schemaLoader.Values();
        }

        public XsType GetType(XName xName) {
            if (xName == null)
                return null;

            var schema = GetSchema(xName.Namespace);
            if (schema == null)
                return null;
            return schema.GetType(xName);
        }
        public XsType GetType(Type type) {
            return Xsd.GetType(type);
        }

        #region Primitive Types
        public XsType AnyType {
            get { return Xsd.GetType("anyType"); }
        }
        public XsType AnySimpleType {
            get { return Xsd.GetType("anySimpleType"); }
        }
        public XsType AnyAtomicType {
            get { return Xsd.GetType("anyAtomicType"); }
        }
        public XsType String {
            get { return Xsd.GetType("string"); }
        }
        public XsType Boolean {
            get { return Xsd.GetType("boolean"); }
        }
        public XsType Decimal {
            get { return Xsd.GetType("decimal"); }
        }
        public XsType Float {
            get { return Xsd.GetType("float"); }
        }
        public XsType Double {
            get { return Xsd.GetType("double"); }
        }
        public XsType Base64Binary {
            get { return Xsd.GetType("base64Binary"); }
        }
        public XsType HexBinary {
            get { return Xsd.GetType("hexBinary"); }
        }
        public XsType AnyUri {
            get { return Xsd.GetType("anyUri"); }
        }
        public XsType QualifiedName {
            get { return Xsd.GetType("qName"); }
        }

        public XsType Duration {
            get { return Xsd.GetType("duration"); }
        }
        public XsType Date {
            get { return Xsd.GetType("date"); }
        }
        public XsType Time {
            get { return Xsd.GetType("time"); }
        }
        public XsType DateTime {
            get { return Xsd.GetType("dateTime"); }
        }
        public XsType Year {
            get { return Xsd.GetType("gYear"); }
        }
        public XsType Month {
            get { return Xsd.GetType("gMonth"); }
        }
        public XsType Day {
            get { return Xsd.GetType("gDay"); }
        }
        public XsType YearMonth {
            get { return Xsd.GetType("gYearMonth"); }
        }
        public XsType MonthDay {
            get { return Xsd.GetType("gMonthDay"); }
        }
        #endregion

        #region BuiltIn Derived Simple Types
        public XsType Integer { 
            get { return Xsd.GetType("integer"); } 
        }
        public XsType Long { 
            get { return Xsd.GetType("long"); } 
        }
        public XsType Int { 
            get { return Xsd.GetType("int"); } 
        }
        public XsType Short { 
            get { return Xsd.GetType("short"); } 
        }
        public XsType Byte { 
            get { return Xsd.GetType("byte"); } 
        }
        public XsType UnsignedLong { 
            get { return Xsd.GetType("unsignedLong"); } 
        }
        public XsType UnsignedInt { 
            get { return Xsd.GetType("unsignedInt"); } 
        }
        public XsType UnsignedShort { 
            get { return Xsd.GetType("unsignedShort"); } 
        }
        public XsType UnsignedByte { 
            get { return Xsd.GetType("unsignedByte"); } 
        }
        public XsType NonPositiveInteger { 
            get { return Xsd.GetType("nonPositiveInteger"); } 
        }
        public XsType NegativeInteger { 
            get { return Xsd.GetType("negativeInteger"); } 
        }
        public XsType NonNegativeInteger { 
            get { return Xsd.GetType("nonNegativeInteger"); } 
        }
        public XsType PositiveInteger { 
            get { return Xsd.GetType("positiveInteger"); } 
        }
        public XsType DateTimeStamp { 
            get { return Xsd.GetType("dateTimeStamp"); } 
        }
        public XsType DayTimeDuration { 
            get { return Xsd.GetType("dayTimeDuration"); } 
        }
        public XsType YearMonthDuration { 
            get { return Xsd.GetType("yearMonthDuration"); } 
        }
        #endregion
    }
    public sealed class XsSchema : XsObject {

        private readonly XsLoader m_loader;
        private readonly XNamespace m_namespace;
        private readonly XsForm m_attributeFormDefault;
        private readonly XsForm m_elementFormDefault;
        private readonly Loader<XName, XsType> m_typeLoader;
        private readonly Loader<string, XsElement> m_rootElementLoader;
        private readonly Loader<XName, XsAttributeGroup> m_attributeGroupLoader;
        private readonly Lazy<Dictionary<XPrefix, XNamespace>> m_xNamespaceByXPrefix;

        #region Constructor
        internal XsSchema(
            XsLoader loader, 
            XNamespace ns,
            XsForm attributeFormDefault,
            XsForm elementFormDefault,
            Lazy<Dictionary<XPrefix, XNamespace>> xNamespaceByXPrefix,
            Func<XsSchema, IEnumerable<object>> typeNames,
            Func<XsTypeFactory, XsType> typeFactory,
            Func<XsSchema, IEnumerable<object>> attributeGroupNames,
            Func<XsAttributeGroupFactory, XsAttributeGroup> attributeGroupFactory,
            Func<XsSchema, IEnumerable<object>> rootElementNames,
            Func<XsRootElementFactory, XsElement> rootElementFactory,

            XsMetadataFactory metadata = null
        ) : base(
            loader: loader,
            metadata: metadata
        ) {

            m_namespace = ns ?? XNamespace.None;
            m_loader = loader;
            m_attributeFormDefault = attributeFormDefault;
            m_elementFormDefault = elementFormDefault;
            m_xNamespaceByXPrefix = xNamespaceByXPrefix;

            m_rootElementLoader = new Loader<string, XsElement>(
                keys: o => rootElementNames(this),
                defaultResolver: o => rootElementFactory(new XsRootElementFactory(this, o.Key)),
                getKeys: o => new object[] {
                    o.Name
                },
                normalizeKey: o => o as string,
                getLoader: o => o.Schema.m_rootElementLoader
            );

            Func<object, XName> normalizeName = o => {
                
                // XName
                var xName = o as XName;
                if (xName == null) {

                    // string
                    var name = o as string;
                    if (name == null)
                        return null;

                    xName = XNamespace + name;
                }

                // namespace must match
                if (xName.Namespace != XNamespace)
                    return null;

                return xName;
            };

            m_attributeGroupLoader = new Loader<XName, XsAttributeGroup>(
                keys: o => attributeGroupNames(this),
                getLoader: o => o.Schema.m_attributeGroupLoader,
                getKeys: o => new object[] {
                    o.XName,
                    o.Name
                },
                normalizeKey: normalizeName,
                defaultResolver: o => attributeGroupFactory(new XsAttributeGroupFactory(this, o.Key))
            );

            m_typeLoader = new Loader<XName, XsType>(
                keys: o => typeNames(this),
                getLoader: o => o.Schema.m_typeLoader,
                getKeys: o => new object[] { 
                    o.XName, 
                    o.Name, 
                    o.Alias 
                },
                normalizeKey: normalizeName,
                defaultResolver: o => typeFactory(new XsTypeFactory(this, o.Key))
            );
        }
        #endregion

        internal XsType GetType(Type type) {
            return m_typeLoader.TryGet(type);
        }

        public XNamespace XNamespace {
            get { return m_namespace; }
        }
        public XsForm AttributeFormDefault {
            get { return m_attributeFormDefault; }
        }
        public XsForm ElementFormDefault {
            get { return m_elementFormDefault; }
        }

        public IEnumerable<XPrefix> Prefixes() {
            return m_xNamespaceByXPrefix.Value.Keys;
        }
        public IEnumerable<XPrefix> Prefixes(XsSchema schema) {
            return Prefixes(schema.XNamespace);
        }
        public IEnumerable<XPrefix> Prefixes(XNamespace xNamespace) {
            return m_xNamespaceByXPrefix.Value
                .Where(o => o.Value == xNamespace).Select(o => o.Key);
        }

        public XQName GetQName(XName xName) {
            if (xName == null)
                return null;

            var prefixes = Prefixes(xName.Namespace);
            
            var prefix = prefixes.OrderBy(o => o.Value).FirstOrDefault();
            if (prefix == null)
                return null;

            return prefix + xName.LocalName;
        }
        public XQName GetQName(XsType xsType) {
            if (xsType == null)
                return null;
            return GetQName(xsType.XName);
        }
        public XsSchema GetSchema(XPrefix prefix) {
            return Loader.GetSchema(GetXNamespace(prefix));
        }
        public XNamespace GetXNamespace(XPrefix prefix) {
            if (prefix == null)
                return null;

            return m_xNamespaceByXPrefix.Value.GetValueOrDefault(prefix);
        }
        public XName GetXName(XQName qName) {
            if (qName == null)
                return null;

            var schema = GetSchema(qName.XPrefix);
            if (schema == null)
                return null;

            return schema.XNamespace + qName.LocalName;
        }

        public IEnumerable<XsType> Types() {
            return m_typeLoader.Values();
        }
        public XsType GetType(XName name) {
            return m_typeLoader.TryGet(name);
        }
        public XsType GetType(string name) {
            return m_typeLoader.TryGet(name);
        }
        public XsType GetType(XQName qName) {
            return Loader.GetType(GetXName(qName));
        }

        public IEnumerable<XsAttributeGroup> AttributeGroups() {
            return m_attributeGroupLoader.Values();
        }
        public XsAttributeGroup GetAttributeGroup(XName name) {
            return m_attributeGroupLoader.TryGet(name);
        }
        public XsAttributeGroup GetAttributeGroup(string name) {
            return m_attributeGroupLoader.TryGet(name);
        }

        public IEnumerable<XsElement> RootElements() {
            return m_rootElementLoader.Values();
        }
        public XsElement GetRootElement(string name) {
            return m_rootElementLoader.TryGet(name);
        }
        public XsElement GetRootElement(XName name) {
            if (name.Namespace != XNamespace)
                return null;
            return GetRootElement(name.LocalName);
        }

        public override string ToString() {
            return XNamespace.ToString();
        }
    }

    public abstract class XsObject {
        internal static readonly XNamespace Xs = XsLoader.XsdXNamespace;

        private XsLoader m_loader;
        private Lazy<XElement> m_appInfo;
        private Lazy<XElement> m_documentation;
        private Lazy<string> m_comment;

        public XsObject(
            XsLoader loader, 
            XsMetadataFactory metadata = null) {

            m_loader = loader;

            if (metadata == null)
                metadata = XsMetadataFactory.Empty;

            m_appInfo = metadata.GetAppInfo(this);
            m_documentation = metadata.GetDocumentation(this);
            m_comment = metadata.GetComment(this);
        }

        public XsLoader Loader {
            get { return m_loader; }
        }
        public XElement AppInfo {
            get { return m_appInfo.Value; }
        }
        public XElement Documentation {
            get { return m_documentation.Value; }
        }
        public string Comment {
            get { return m_comment.Value; }
        }
    }

    public abstract class XsType : XsObject {
        private readonly XName m_xName;
        private readonly XsSchema m_schema;
        private readonly XsFinal m_final;
        private readonly XsType m_baseType;
        private readonly XsType m_declaringType;

        internal XsType(
            XsSchema schema,
            XsType baseType,
            XsFinal final = XsFinal.None,
            XsType declaringType = null,
            XName xName = null,
            XsMetadataFactory metadata = null
        ) : base(
            loader: schema.Loader, 
            metadata: metadata
        ) {
            m_schema = schema;
            m_xName = xName;
            m_baseType = baseType;
            m_declaringType = declaringType;

            if (m_baseType != null) 
                m_final = final | BaseType.Final;
        }

        #region Serialization
        internal bool TestLexicalFacets(string literal, bool throwOnError) {
            foreach (var facet in Facets())
                if (!facet.TestLiteral(literal, throwOnError))
                    return false;
            return true;
        }
        internal bool TestValueFacets(object value, bool throwOnError) {
            foreach (var facet in Facets())
                if (!facet.TestValue(value, throwOnError))
                    return false;
            return true;
        }
        internal virtual object CustomDeserialize(string literal, bool throwOnError = true) {
            throw new InvalidOperationException();
        }
        internal virtual string CustomSerialize(object primitiveValue, bool throwOnError = true) {
            throw new InvalidOperationException();
        }
        #endregion

        #region Names
        public virtual Type Alias {
            get { return null; }
        }
        public string Name {
            get {
                if (XName == null)
                    return null;
                return XName.LocalName; 
            }
        }
        public XName XName {
            get { return m_xName; }
        }
        public XNamespace Namespace {
            get { return XName.Namespace; }
        }
        public bool IsXsdType {
            get { return Namespace == Xs; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        #endregion

        #region Type
        public bool IsUr {
            get {
                var isAnyType = this is XsFundamentalType.AnyType;
                var isAnySimpleType = this is XsFundamentalType.AnySimpleType;
                var isAnyAtomicType = this is XsFundamentalType.AnyAtomicType;
                return isAnyType || isAnySimpleType || isAnyAtomicType;
            }
        }

        public virtual bool IsAtomicType {
            get { return BaseType.IsAtomicType; }
        }
        public virtual bool IsSimpleType {
            get { return BaseType.IsSimpleType; }
        }

        public bool IsList {
            get {
                if (this is XsListSimpleType)
                    return true;

                if (BaseType == null)
                    return false;

                return BaseType.IsList;
            }
        }
        public bool IsUnion {
            get { 
                if (this is XsUnionSimpleType)
                    return true;

                if (BaseType == null)
                    return false;

                return BaseType.IsUnion; 
            }
        }

        public bool IsComplexType {
            get { return this is XsComplexType; }
        }
        public bool IsGroup {
            get { return this is XsGroupType; }
        }

        public bool IsSubclassOf(XsType type) {
            return BaseType.IsOrIsSubclassOf(type);
        }
        public bool IsOrIsSubclassOf(XsType type) {
            if (this == type)
                return true;

            if (BaseType == null)
                return false;

            return BaseType.IsOrIsSubclassOf(type);
        }
        #endregion

        #region Derivation
        public XsType BaseType {
            get { return m_baseType; }
        }
        public virtual XsType ListType {
            get {
                if (BaseType != null)
                    return BaseType.ListType;
                return null;
            }
        }
        public virtual IEnumerable<XsType> UnionTypes(bool declaredOnly = false) {
            if (BaseType != null)
                return BaseType.UnionTypes(declaredOnly);
            return Enumerable.Empty<XsType>();
        }

        public virtual bool IsAbstract {
            get { return false; }
        }
        public virtual bool IsExtension {
            get { return false; }
        }
        public virtual bool IsRestriction {
            get { return !IsExtension; }
        }
        public virtual bool IsMixed {
            get { return false; }
        }
        public virtual bool HasSimpleContent {
            get { return false; }
        }
        public virtual bool HasComplexContent {
            get { return false; }
        }

        public XsFinal Final {
            get { return m_final; }
        }
        public bool IsFinalRestriction {
            get { return (Final & XsFinal.Restriction) != 0; }
        }
        public bool IsFinalExtension {
            get { return (Final & XsFinal.Extension) != 0; }
        }
        public bool IsFinalList {
            get { return (Final & XsFinal.List) != 0; }
        }
        public bool IsFinalUnion {
            get { return (Final & XsFinal.Union) != 0; }
        }
        #endregion

        #region Facets
        public virtual XsCardinality Cardinality {
            get { return XsCardinality.Finite; }
        }
        public virtual XsOrdered Orderd {
            get { return XsOrdered.False; }
        }
        public virtual bool IsBounded {
            get { return false; }
        }
        public virtual bool IsNumeric {
            get { return false; }
        }
        
        public virtual IEnumerable<XsFacet> Facets(XsFacetType? type = null, bool declaredOnly = false) {
            return Enumerable.Empty<XsFacet>();
        }
        public virtual XsFacet GetFacet(XsFacetType facetType) {
            return null;
        }
        public dynamic GetFacetValue(XsFacetType facetType) {
            var facet = GetFacet(facetType);
            if (facet == null)
                return null;
            return facet.Value;
        }

        public virtual XsFacetType ApplicableFacets {
            get { return BaseType.ApplicableFacets; }
        }
        public XsFacetType FixedFacets {
            get { return Facets().Where(o => o.IsFixed).Aggregate(default(XsFacetType), (a, o) => a | o.FacetType); }
        }
        public XsFacetType AppliedFacets {
            get { return Facets().Aggregate(default(XsFacetType), (a, o) => a | o.FacetType); }
        }
        public XsFacetType OpenFacets {
            get { return ApplicableFacets & ~FixedFacets; }
        }
        #endregion

        #region Serialization
        public virtual Type ClrType {
            get { return BaseType.ClrType; }
        }
        public virtual object Deserialize(string literal, bool throwOnError = true) {
            throw new InvalidOperationException();
        }
        public virtual string Serialize(object value, bool throwOnError = true) {
            throw new InvalidOperationException();
        }

        public virtual bool IsStreamable {
            get { return false; }
        }
        public virtual TextReader Deserialize(TextReader stream) {
            throw new InvalidOperationException();
        }
        #endregion

        #region ComplexType
        public virtual XsType SimpleContent {
            get { return null; }
        }
        public virtual XsComplexContent ComplexContent {
            get { return null; }
        }
        public virtual IEnumerable<XsAttribute> Attributes(bool declaredOnly = false) {
            yield break;
        }
        public virtual IEnumerable<XsAttributeGroup> AttributeGroups() {
            yield break;
        }
        #endregion

        public override string ToString() {
            if (XName == null)
                return "(anonymous type)";
            return XName.ToString();
        }
    }
    internal static class XsFundamentalType {
        internal sealed class AnyType : XsType {
            internal AnyType(XsSchema schema)
                : base(
                    schema: schema, 
                    xName: Xs + "anyType", 
                    baseType: null) {
            }

            public override bool IsAbstract {
                get { return true; }
            }
            public override Type ClrType {
                get { return null; }

            }
            public override XsFacetType ApplicableFacets {
                get { return XsFacetType.None; }
            }
            public override Type Alias {
                get { return typeof(object); }
            }

            public override bool IsRestriction {
                get { return false; }
            }
            public override bool IsExtension {
                get { return false; }
            }
            public override bool IsAtomicType {
                get { return false; }
            }
            public override bool IsSimpleType {
                get { return false; }
            }
        }
        internal sealed class AnySimpleType : XsSimpleType {
            internal AnySimpleType(XsSchema schema)
                : base(
                schema: schema, 
                xName: Xs + "anySimpleType", 
                baseType: schema.Loader.AnyType) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                return literal;
            }

            public override bool IsSimpleType {
                get { return true; }
            }
            public override Type ClrType {
                get { return typeof(string); }
            }
        }
        internal sealed class AnyAtomicType : XsSimpleType {
            internal AnyAtomicType(XsSchema schema)
                : base(
                schema: schema, 
                xName: Xs + "anyAtomicType", 
                baseType: schema.Loader.AnySimpleType) {
            }

            public override bool IsSimpleType {
                get { return true; }
            }
            public override bool IsAtomicType {
                get { return true; }
            }
        }

        internal abstract class PrimitiveSimpleType : XsSimpleType {
            private XsFacetType m_applicableConstrainingFacets;

            internal PrimitiveSimpleType(
                XsSchema schema,
                XName xName,
                XsFacetType applicableConstrainingFacets,
                Type alias = null,
                Func<XsFacetFactory, IEnumerable<XsFacet>> getFacets = null)

                : base(
                    schema: schema, 
                    xName: xName, 
                    baseType: schema.Loader.AnyAtomicType,
                    alias: alias,
                    getFacets: getFacets != null ? getFacets : o => new[] {
                        o.CreateWhitespaceFacet(XsWhitespaceFacetType.Collapse, isFixed: true)
                    }
                ) {

                m_applicableConstrainingFacets = applicableConstrainingFacets;
            }

            internal override string CustomSerialize(object primitiveValue, bool throwOnError) {
                return primitiveValue.ToString();
            }

            public override XsFacetType ApplicableFacets {
                get { return m_applicableConstrainingFacets; }
            }
        }

        internal sealed class String : PrimitiveSimpleType {
            internal String(XsSchema schema)
                : base(
                    schema: schema, 
                    xName: Xs + "string", 
                    applicableConstrainingFacets: XsFacet.StringFacets, 
                    alias: typeof(string),
                    getFacets: o => new[] {
                        o.CreateWhitespaceFacet(XsWhitespaceFacetType.Preserve)
                    }
                ) { }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                return literal;
            }

            public override Type ClrType {
                get { return typeof(string); }
            }
        }
        internal sealed class Boolean : PrimitiveSimpleType {
            internal Boolean(XsSchema schema)
                : base(schema, Xs + "boolean", XsFacet.BooleanFacets, typeof(bool)) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError) {

                if (literal == "1" || literal == "true")
                    return true;

                else if (literal == "0" || literal == "false")
                    return false;

                if (throwOnError)
                    throw new XsSerializationException(
                        "Literal value '{0}' could not be deserialized into a boolean."
                        .Substitute(literal)
                    );

                return null;
            }
            internal override string CustomSerialize(object value, bool throwOnError) {
                return (bool)value ? "true" : "false";
            }

            public override Type ClrType {
                get { return typeof(bool); }
            }
        }
        internal sealed class Decimal : PrimitiveSimpleType {
            internal Decimal(XsSchema schema)
                : base(schema, Xs + "decimal", XsFacet.DecimalFacets, typeof(decimal)) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                if (throwOnError)
                    return decimal.Parse(literal);

                decimal result;
                if (!decimal.TryParse(literal, out result))
                    return null;
                return result;
            }

            public override Type ClrType {
                get { return typeof(decimal); }
            }
        }
        internal sealed class Float : PrimitiveSimpleType {
            internal Float(XsSchema schema)
                : base(schema, Xs + "float", XsFacet.NumericalFacets, typeof(float)) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                if (throwOnError)
                    return float.Parse(literal);

                float result;
                if (!float.TryParse(literal, out result))
                    return null;
                return result;
            }

            public override Type ClrType {
                get { return typeof(float); }
            }
        }
        internal sealed class Double : PrimitiveSimpleType {
            internal Double(XsSchema schema)
                : base(schema, Xs + "double", XsFacet.NumericalFacets, typeof(double)) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                if (throwOnError)
                    return double.Parse(literal);

                double result;
                if (!double.TryParse(literal, out result))
                    return null;
                return result;
            }

            public override Type ClrType {
                get { return typeof(double); }
            }
        }

        internal sealed class Duration : PrimitiveSimpleType {
            internal Duration(XsSchema schema)
                : base(schema, Xs + "duration", XsFacet.DurationFacets) {
            }
        }

        internal abstract class TimeLineType : PrimitiveSimpleType {
            internal const string YearRx = @"(?<year>\d\d\d\d)";
            internal const string MonthRx = @"(?<month>\d\d)";
            internal const string DayRx = @"(?<day>\d\d)";
            internal const string HourRx = @"(?<hour>\d\d)";
            internal const string MinuteRx = @"(?<minute>\d\d)";
            internal const string SecondRx = @"(?<second>\d\d)";
            internal const string MillisecondsRx = @"(?<milliseconds>\d+)";
            internal const string TimezoneRx = @"((?<timezoneZulu>Z)|((?<timezoneSign>\+|\-)(?<timezoneHour>\d\d):(?<timezoneMinute>\d\d)))";

            private const int MaxTimezoneOffset = 14 * 60;
            private const string EndOfDayLiteral = "24:00:00";
            private const string StartOfDayLiteral = "00:00:00";

            private string m_timeSpanRegex;
            private string m_format;

            internal TimeLineType(XsSchema schema, XName xName, string regexFormat, string format, Type alias = null)
                : base(
                    schema: schema, 
                    xName: xName, 
                    applicableConstrainingFacets: XsFacet.TimeLineFacets,
                    alias: alias
                ) {

                m_format = format;
                m_timeSpanRegex = string.Format("^" + regexFormat + "$",
                    YearRx, MonthRx, DayRx,
                    HourRx, MinuteRx, SecondRx, MillisecondsRx,
                    TimezoneRx
                );
            }

            internal sealed override object CustomDeserialize(string literal, bool throwOnError) {

                // match literal
                var match = Regex.Match(literal, m_timeSpanRegex);
                if (!match.Success) {
                    if (!throwOnError)
                        return null;

                    throw new XsSerializationException(
                        "Literal '{0}' could not be deserialized into a '{1}'."
                        .Substitute(literal, this)
                    );
                }

                // parse date
                var year = match.Get<int?>("year");
                var month = match.Get<int?>("month");
                var day = match.Get<int?>("day");

                // parse time
                var hour = match.Get<int?>("hour");
                var minute = match.Get<int?>("minute");
                var second = match.Get<int?>("second");
                var milliseconds = match.Get<int?>("milliseconds");

                // normalize end-of-day
                var endOfDay = false;
                if (literal.Contains(EndOfDayLiteral)) {
                    hour = 0;
                    if (minute != 0 || second != 0 || milliseconds != 0)
                        throw new ArgumentException();
                    endOfDay = true;
                }

                // parse offset
                var timezoneZulu = match.Get("timezoneZulu") != null;
                var timezoneSign = match.Get("timezoneSign");

                // result as DateTimeOffset
                if (timezoneZulu || timezoneSign != null) {

                    // compute offset as TimeSpan
                    var sign = timezoneSign == "+" ? 1 : -1;
                    var offset = TimeSpan.FromMinutes(timezoneZulu ? 0 :
                        sign * (match.Get<int>("timezoneHour") * 60 + match.Get<int>("timezoneMinute")));

                    // verify offset bounds
                    var offsetTotalMinutes = offset.Hours * 60 + offset.Minutes;
                    if (offsetTotalMinutes > MaxTimezoneOffset || offsetTotalMinutes < -MaxTimezoneOffset)
                        return false;

                    // construct DateTimeSpan
                    var min = DateTimeOffset.MinValue;                    
                    var dateTimeOffset = new DateTimeOffset(
                        year ?? min.Year, month ?? min.Month, day ?? min.Day,
                        hour ?? min.Hour, minute ?? min.Minute, second ?? min.Second, milliseconds ?? min.Millisecond,
                        offset);

                    // end-of-day fixup
                    if (endOfDay)
                        dateTimeOffset = dateTimeOffset.AddDays(1);

                    return new XsDateTime(dateTimeOffset);
                }

                // result as DateTime
                else {
                    var min = System.DateTime.MinValue;
                    var dateTime = new System.DateTime(
                        year ?? min.Year, month ?? min.Month, day ?? min.Day,
                        hour ?? min.Hour, minute ?? min.Minute, second ?? min.Second, milliseconds ?? min.Millisecond,
                        DateTimeKind.Unspecified);

                    // end-of-day fixup
                    if (endOfDay)
                        dateTime = dateTime.AddDays(1);

                    return new XsDateTime(dateTime);
                }
            }
            internal sealed override string CustomSerialize(object value, bool throwOnError) {

                var xsDateTime = (XsDateTime)value;

                // DateTime
                if (xsDateTime.IsDateTime) {
                    var dateTime = (SysDateTime)xsDateTime;
                    return dateTime.ToString(string.Format(m_format, "'.'fff", string.Empty));
                }

                // DateTimeSpan
                var dateTimeSpan = (DateTimeOffset)xsDateTime;
                return dateTimeSpan.ToString(string.Format(m_format, "'.'fff", "zzz"));
            }

            public override Type ClrType {
                get { return typeof(XsDateTime); }
            }
        }
        internal sealed class DateTime : TimeLineType {
            internal DateTime(XsSchema schema)
                : base(schema, Xs + "dateTime",
                    @"{0}\-{1}\-{2}T{3}:{4}:{5}(\.{6})?{7}?",
                    @"yyyy'-'MM'-'dd'T'HH':'mm':'ss{0}{1}",
                typeof(XsDateTime)) {
            }
        }
        internal sealed class Time : TimeLineType {
            internal Time(XsSchema schema)
                : base(schema, Xs + "time",
                    @"{3}:{4}:{5}(\.{6})?{7}?",
                    @"HH':'mm':'ss{0}{1}") {
            }
        }
        internal sealed class Date : TimeLineType {
            internal Date(XsSchema schema)
                : base(schema, Xs + "date",
                    @"{0}\-{1}\-{2}{7}?",
                    @"yyyy'-'MM'-'dd{1}") {
            }
        }
        internal sealed class YearMonth : TimeLineType {
            internal YearMonth(XsSchema schema)
                : base(schema, Xs + "gYearMonth",
                    @"{0}\-{1}{7}?",
                    @"yyyy'-'MM{1}") {
            }
        }
        internal sealed class Year : TimeLineType {
            internal Year(XsSchema schema)
                : base(schema, Xs + "gYear", @"{0}{7}?", @"yyyy{1}") {
            }
        }
        internal sealed class MonthDay : TimeLineType {
            internal MonthDay(XsSchema schema)
                : base(schema, Xs + "gMonthDay", @"\-\-{1}\-{2}{7}?", @"'--'MM'-'dd{1}") {
            }
        }
        internal sealed class Day : TimeLineType {
            internal Day(XsSchema schema)
                : base(schema, Xs + "gDay", @"\-\-\-{2}{7}?", @"'---'dd{1}") {
            }
        }
        internal sealed class Month : TimeLineType {
            internal Month(XsSchema schema)
                : base(schema, Xs + "gMonth", @"\-\-{1}{7}?", @"'--'MM{1}") {
            }
        }

        internal sealed class HexBinary : PrimitiveSimpleType {
            internal HexBinary(XsSchema schema)
                : base(schema, Xs + "hexBinary", XsFacet.StringFacets) {
            }

            public override Type ClrType {
                get { return typeof(byte[]); }
            }
        }
        internal sealed class Base64Binary : PrimitiveSimpleType {
            internal Base64Binary(XsSchema schema)
                : base(schema, Xs + "base64Binary", XsFacet.StringFacets) {
            }

            public override Type ClrType {
                get { return typeof(byte[]); }
            }
        }

        internal sealed class AnyUri : PrimitiveSimpleType {
            internal AnyUri(XsSchema schema)
                : base(schema, Xs + "anyUri", XsFacet.StringFacets, typeof(Uri)) {
            }

            internal override object CustomDeserialize(string literal, bool throwOnError = true) {
                if (throwOnError)
                    return new Uri(literal, UriKind.Absolute);

                Uri result;
                if (!Uri.TryCreate(literal, UriKind.Absolute, out result))
                    return null;
                return result;
            }
            public override Type ClrType {
                get { return typeof(Uri); }
            }
        }
        internal sealed class QualifiedName : PrimitiveSimpleType {
            internal QualifiedName(XsSchema schema)
                : base(schema, Xs + "qName", XsFacet.StringFacets, typeof(XName)) {
            }

            public override Type ClrType {
                get { return typeof(XName); }
            }
        }
    }

    internal abstract class XsSimpleType : XsType {
        private readonly Loader<XsFacetType, XsFacet> m_facetLoader;
        private readonly Type m_alias;

        internal XsSimpleType(
            XsSchema schema,
            XsType baseType,
            Func<XsFacetFactory, IEnumerable<XsFacet>> getFacets = null,
            Type alias = null,
            XsFinal final = XsFinal.None,
            XsType declaringType = null,
            XName xName = null, 
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            declaringType: declaringType,
            baseType: baseType,
            final: final,
            metadata: metadata
        ) {
            ASSERT.Condition(baseType.IsSimpleType || IsUr);

            m_alias = alias;

            var facetByFacetType = new Lazy<Dictionary<XsFacetType, XsFacet>>(() => {
                if (getFacets == null)
                    return new Dictionary<XsFacetType, XsFacet>();

                var facets = getFacets(new XsFacetFactory(this)).ToArray();

                var duplicateFacets = facets.FirstDuplicate(o => o.FacetType);
                if (duplicateFacets.Any())
                    throw new XsLoaderException(
                        "Facet {0} is defined more than once on type {1}."
                        .Substitute(duplicateFacets.First().FacetType, this)
                    );

                return facets.ToDictionary(o => o.FacetType);
            });

            m_facetLoader = new Loader<XsFacetType, XsFacet>(
                keys: o => facetByFacetType.Value.Keys.Cast<object>(),
                defaultResolver: o => facetByFacetType.Value.GetValueOrDefault(o.Key),
                getLoader: o => ((XsSimpleType)o.DeclaringType).m_facetLoader,
                getKeys: o => new object[] { o.FacetType },
                normalizeKey: o => (XsFacetType)o,

                getBaseType: o => BaseType.ToLinkList(x => x.BaseType)
                    .OfType<XsSimpleType>().Select(x => x.m_facetLoader).FirstOrDefault(),
                getOverride: o => o.ConstrainedFacet
            );
        }

        internal override object CustomDeserialize(string literal, bool throwOnError = true) {
            return BaseType.CustomDeserialize(literal, throwOnError);
        }
        internal override string CustomSerialize(object primitiveValue, bool throwOnError = true) {
            return BaseType.CustomSerialize(primitiveValue, throwOnError);
        }

        public sealed override Type Alias {
	        get { return m_alias; }
        }

        public override IEnumerable<XsFacet> Facets(XsFacetType? type = null, bool declaredOnly = false) {
            return m_facetLoader.Values(type, declaredOnly);
        }
        public override XsFacet GetFacet(XsFacetType facetType) {
            return m_facetLoader.TryGet(facetType);
        }

        // clr value <=> string
        public override object Deserialize(string literal, bool throwOnError = true) {

            // normalized literal; possibly trimmed string delimited by single spaces. see WhiteSpace facet
            var whitespace = GetFacetValue(XsFacetType.Whitespace);

            // normalize whitespace
            if (whitespace == XsWhitespaceFacetType.Replace)
                literal = Regex.Replace(literal, @"\s", " ");

            // normalize whitespace, replace contiguous whitespace with a space, trim leading and trailing whitespace
            else if (whitespace == XsWhitespaceFacetType.Collapse) {
                literal = Regex.Replace(literal, @"\s+", " ");
                literal = literal.Trim();
            }

            // constrained literal; literal that does not violate any constraining facets
            if (!TestLexicalFacets(literal, throwOnError))
                return null;

            var value = CustomDeserialize(literal, throwOnError);
            if (value == null)
                return null;

            if (!TestValueFacets(value, throwOnError))
                return null;

            return value;
        }
        public override string Serialize(object value, bool throwOnError = true) {

            // value; value the user provided
            if (value == null)
                throw new XsSerializationException(
                    "Cannot serialize null value.");

            // the type of the value must match the clr type
            if (!ClrType.IsAssignableFrom(value)) {
                if (!throwOnError)
                    return null;

                throw new XsSerializationException();
            }

            // test for violdated facets
            if (!TestValueFacets(value, throwOnError))
                return null;

            // format value (e.g. DateTimeSpan => "2012-01-01+00:0")
            var literal = CustomSerialize(value, throwOnError);
            if (literal == null)
                return null;

            // ensure literal is in lexical space
            if (!TestLexicalFacets(literal, throwOnError))
                return null;

            return literal;
        }

        public override bool IsStreamable {
            get {
                var streamableType =
                    IsOrIsSubclassOf(Loader.String) ||
                    IsOrIsSubclassOf(Loader.HexBinary) ||
                    IsOrIsSubclassOf(Loader.Base64Binary);

                var streamableFacets = XsFacet.CardinalityFacets | XsFacet.LengthFacets;

                // true if a string or binary whose only facets have todo with length
                return streamableType && (AppliedFacets & ~streamableFacets) == 0;
            }
        }
    }
    internal sealed class XsBuiltInSimpleType : XsSimpleType {
        private readonly Type m_clrType;
        private readonly Func<string, bool, object> m_parseLiteral;

        internal XsBuiltInSimpleType(
            XsSchema schema,
            XName xName,
            Type clrType,
            XsType baseType,
            Func<XsFacetFactory, IEnumerable<XsFacet>> getFacets,
            Func<string, bool, object> parseLiteral = null,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName, 
            baseType: baseType, 
            getFacets: getFacets,
            metadata: metadata,
            alias: clrType
        ) {
            m_parseLiteral = parseLiteral;
            m_clrType = clrType;
        }

        internal override object CustomDeserialize(string literal, bool throwOnError = true) {
            if (m_parseLiteral == null)
                return base.CustomDeserialize(literal, throwOnError);
            return m_parseLiteral(literal, throwOnError);
        }

        public override Type ClrType {
            get { return m_clrType ?? BaseType.ClrType; }
        }
    }
    internal sealed class XsConstrainedSimpleType : XsSimpleType {

        internal XsConstrainedSimpleType(
            XsSchema schema,
            XsType baseType,
            Func<XsFacetFactory, IEnumerable<XsFacet>> getFacets,
            XsType declaringType = null,
            XName xName = null,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            declaringType: declaringType,
            baseType: baseType, 
            getFacets: getFacets,
            metadata: metadata
        ) {
        }
    }
    internal sealed class XsListSimpleType : XsSimpleType {
        private XsType m_listType;

        internal XsListSimpleType(
            XsSchema schema,
            XName xName,
            XsType listType,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName, 
            baseType: schema.Loader.AnySimpleType,
            metadata: metadata
        ) {

            m_listType = listType;

            if (m_listType.IsList || m_listType.UnionTypes().Any(o => o.IsList))
                throw new XsLoaderException(
                   ("List type '{0}' cannot have an item type that is a list " + 
                    "or a union with a list as content.")
                    .Substitute(this));
        }

        internal override object CustomDeserialize(string literal, bool throwOnError = true) {
            var tokens = Regex.Split(literal, @"\s");
            
            var result = tokens.Select(o => m_listType.Deserialize(o, throwOnError));
            if (result.Any(o => o == null))
                return null;

            var typedResult = result.Cast(m_listType.ClrType);
            return typedResult;
        }
        internal override string CustomSerialize(object value, bool throwOnError = true) {
            var list = value as IEnumerable;

            if (list == null) {
                if (throwOnError)
                    throw new XsSerializationException(
                        "Value '{0}' is not IEnumerable and so cannot be serialized as a list"
                        .Substitute(this));
                return null;
            }

            var result = list.Cast<object>().StringJoin(" ", o => m_listType.Serialize(o));
            return result;
        }

        public override Type ClrType {
            get { return typeof(IEnumerable<>).MakeGenericType(m_listType.ClrType); }
        }
        public override XsType ListType {
            get { return m_listType; }
        }
        public override XsFacetType ApplicableFacets {
            get { return XsFacet.ListRestrictionFacets; }
        }
    }
    internal sealed class XsUnionSimpleType : XsSimpleType {
        private IEnumerable<XsType> m_unionTypes;

        internal XsUnionSimpleType(
            XsSchema schema,
            XName xName,
            IEnumerable<XsType> unionTypes,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName, 
            baseType: schema.Loader.AnySimpleType,
            metadata: metadata
        ) {

            m_unionTypes = unionTypes;

            var urContent = m_unionTypes.Where(o => o.IsUr).ToArray();
            if (urContent.Any())
                throw new XsLoaderException(
                    "Union type '{0}' cannot have ur type '{1}' as content."
                    .Substitute(this, urContent.First())
                );

            var nonSimpleTypeContent = m_unionTypes.Where(o => !o.IsSimpleType).ToArray();
            if (nonSimpleTypeContent.Any())
                throw new XsLoaderException(
                    "Union type '{0}' cannot have non simple type '{1}' as content."
                    .Substitute(this, nonSimpleTypeContent.First())
                );
        }

        internal override object CustomDeserialize(string literal, bool throwOnError = true) {

            // first content type to successfully parse literal wins
            foreach (var contentType in UnionTypes()) {

                // deserialize = test lexical + parse literal + test value; all must pass
                var result = contentType.Deserialize(literal, throwOnError: false);
                if (result != null)
                    return new XsTypedValue(contentType, result);
            }

            // all content types failed to parse literal
            if (throwOnError)
                throw new XsSerializationException(
                    "Literal '{0}' could not be deserialized for any element type of union '{1}'."
                    .Substitute(literal, this));

            return null;
        }
        internal override string CustomSerialize(object value, bool throwOnError = true) {

            // type is supplied
            var typedValue = value as XsTypedValue;
            if (typedValue != null) 
                return typedValue.Type.Serialize(typedValue.Value);

            // first content type to successfully format value wins
            foreach (var contentType in UnionTypes()) {

                // serialize = test value + format value + test lexical; all must pass
                var result = contentType.Serialize(value, throwOnError: false);
                if (result != null)
                    return result;
            }

            // all content types failed to parse literal
            if (throwOnError)
                throw new XsSerializationException(
                    "Value '{0}' could not be serialized into any element type of union '{1}'."
                    .Substitute(value, this));

            return null;
        }

        public override Type ClrType {
            get { return typeof(object); }
        }
        public override IEnumerable<XsType> UnionTypes(bool declaredOnly = false) {

            foreach (var contentType in m_unionTypes) {

                if (!declaredOnly && contentType.IsUnion) {
                    foreach (var subUnionType in contentType.UnionTypes(declaredOnly))
                        yield return subUnionType;
                }

                else
                    yield return contentType;
            }
        }
        public override XsFacetType ApplicableFacets {
            get { return XsFacet.UnionRestrictionFacets; }
        }
    }

    internal abstract class XsComplexType : XsType {
        private bool m_isRestriction;
        private bool m_isAbstract;
        private bool m_isMixed;
        private Lazy<IEnumerable<object>> m_attributes;

        internal XsComplexType(
            XsSchema schema, 
            XsType baseType,
            bool isRestriction,
            bool isAbstract,
            bool isMixed,
            XsFinal final,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,

            XsType declaringType = null,
            XName xName = null,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            declaringType: declaringType,
            baseType: baseType,
            final: final,
            metadata: metadata
        ) {
            ASSERT.Condition(BaseType.IsUr || BaseType.IsComplexType);

            m_isRestriction = isRestriction;
            m_isMixed = isMixed;
            m_isAbstract = isAbstract;

            m_attributes = new Lazy<IEnumerable<object>>(() => 
                attributeFactory(new XsAttributeFactory(Schema, this)).ToList()
            );
        }

        public override bool IsExtension {
            get { return !IsRestriction; }
        }
        public override bool IsAbstract {
            get { return m_isAbstract; }
        }
        public override bool IsMixed {
            get { return m_isMixed; }
        }

        public override IEnumerable<XsAttribute> Attributes(bool declaredOnly = false) {
            foreach (var attribute in m_attributes.Value.OfType<XsAttribute>())
                yield return attribute;

            if (declaredOnly)
                yield break;

            foreach (var attribute in AttributeGroups().SelectMany(o => Attributes(declaredOnly: false)))
                yield return attribute;
        }
        public override IEnumerable<XsAttributeGroup> AttributeGroups() {
            return m_attributes.Value.OfType<XsAttributeGroup>();
        }
    }
    internal sealed class XsComplexTypeWithComplexContent : XsComplexType {
        private Lazy<XsComplexContent> m_complexContent;

        internal XsComplexTypeWithComplexContent(
            XsSchema schema, 
            XsType baseType,
            bool isRestriction,
            bool isAbstract,
            bool isMixed,
            XsFinal final,
            Func<XsComplexContentFactory, XsComplexContent> complexContentFactory,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,

            XsType declaringType = null,
            XName xName = null,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            declaringType: declaringType,
            baseType: baseType,
            isRestriction: isRestriction,
            isAbstract: isAbstract,
            isMixed: isMixed,
            final: final,
            attributeFactory: attributeFactory,
            metadata: metadata
        ) {

            m_complexContent = new Lazy<XsComplexContent>(() => 
                complexContentFactory(new XsComplexContentFactory(this))
            );
        }

        public override bool HasComplexContent {
            get { return true; }
        }

        public override XsComplexContent ComplexContent {
            get { return m_complexContent.Value; }
        }
    }
    internal sealed class XsComplexTypeWithSimpleContent : XsComplexType {
        private readonly Lazy<XsType> m_simpleContent;

        internal XsComplexTypeWithSimpleContent(
            XsSchema schema, 
            XsType baseType,
            bool isRestriction,
            bool isAbstract,
            XsFinal final,
            Func<XsTypeFactory, XsType> simpleContent,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,

            XsType declaringType = null,
            XName xName = null, 
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            declaringType: declaringType,
            baseType: baseType,
            isRestriction: isRestriction,
            isAbstract: isAbstract,
            isMixed: false,
            final: final,
            attributeFactory: attributeFactory,
            metadata: metadata
        ) {

            m_simpleContent = new Lazy<XsType>(() => {
                var simpleType = simpleContent(new XsTypeFactory(schema, this));
                ASSERT.Condition(simpleType.IsSimpleType);
                return simpleType;
            });
        }

        public override XsType SimpleContent {
            get { return this.m_simpleContent.Value; }
        }
        public override bool HasSimpleContent {
            get { return true; }
        }
    }

    internal sealed class XsGroupType : XsType {

        internal XsGroupType(
            XsSchema schema, 
            XName xName,
            XsMetadataFactory metadata = null
        ) : base(
            schema: schema, 
            xName: xName,
            baseType: null,
            metadata: metadata
        ) {
        }
    }

    public sealed class XsAttributeGroup : XsObject {
        private Lazy<IEnumerable<object>> m_attributes;
        private XName m_xName;
        private XsSchema m_schema;

        internal XsAttributeGroup(
            XsSchema schema, 
            XName xName,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,
            XsMetadataFactory metadata = null
        ) : base(
            loader: schema.Loader,
            metadata: metadata
        ) {

            m_xName = xName;
            m_attributes = new Lazy<IEnumerable<object>>(() => 
                attributeFactory(new XsAttributeFactory(Schema, this)).ToList()
            );
        }

        public string Name {
            get { return XName.LocalName; }
        }
        public XName XName {
            get { return m_xName; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }

        public IEnumerable<XsAttribute> Attributes(bool declaredOnly = false) {
            foreach (var attribute in m_attributes.Value.OfType<XsAttribute>())
                yield return attribute;

            if (declaredOnly)
                yield break;

            foreach (var attribute in AttributeGroups().SelectMany(o => Attributes(declaredOnly: false)))
                yield return attribute;
        }
        public IEnumerable<XsAttributeGroup> AttributeGroups() {
            return m_attributes.Value.OfType<XsAttributeGroup>();
        }

        public XObject ToXsd() {
            throw new NotImplementedException();
        }
    }
    public sealed class XsAttribute : XsObject {
        private readonly XsAttributeGroup m_declaringAttributeGroup;
        private readonly XsType m_declaringType;
        private readonly XsSchema m_schema;

        // named attribute
        private readonly string m_name;
        private readonly XsForm m_form;
        private readonly XsType m_type;
        private readonly XsAttributeUse m_use;
        private readonly object m_default;

        // any attribute
        private readonly XsProcessContents m_processContents;
        private readonly XsNamespaceType m_namespaceType;
        private readonly IEnumerable<XNamespace> m_namespaces;

        #region Constructors
        public XsAttribute(
            XsSchema schema,
            XsType declaringType,
            XsAttributeGroup declaringAttributeGroup,

            string name,
            XsForm form,
            XsType type,
            XsAttributeUse use,
            object @default,

            XsMetadataFactory metadata = null
        ) : this(
            schema: schema, 
            declaringType: declaringType,
            metadata: metadata
        ) {
            if (form == XsForm.Undefined)
                form = schema.AttributeFormDefault;

            m_form = form;
            m_name = name;
            m_declaringAttributeGroup = declaringAttributeGroup;
            m_type = type;
            m_use = use;
            m_default = @default;
        }

        // anyAttribute
        public XsAttribute(
            XsSchema schema,
            XsType declaringType,
            XsAttributeGroup declaringAttributeGroup,

            XsProcessContents processContents,
            IEnumerable<XNamespace> namespaces,
            XsNamespaceType namespaceType,

            XsMetadataFactory metadata
        ) : this(
            schema: schema,
            declaringType: declaringType,
            metadata: metadata
        ) {
            m_declaringAttributeGroup = declaringAttributeGroup;
            m_namespaces = namespaces ?? Enumerable.Empty<XNamespace>();
            m_namespaceType = namespaceType;
            m_processContents = processContents;
        }

        private XsAttribute(
            XsSchema schema,
            XsType declaringType,
            XsMetadataFactory metadata = null
        ) : base(
            loader: schema.Loader,
            metadata: metadata
        ) {
            m_schema = schema;
            m_declaringType = declaringType;
        }
        #endregion

        public XsAttributeGroup DeclaringAttributeGroup {
            get { return m_declaringAttributeGroup; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }

        // named attribute
        public string Name {
            get { return m_name; }
        }
        public XsForm Form {
            get { return m_form; }
        }
        public XName XName {
            get {
                if (Name == null)
                    return null;

                var xNamespace = Form == XsForm.Unqualified ? XNamespace.None : Schema.XNamespace;
                var xName = xNamespace + Name;
                return xName;
            }
        }
        public XsAttributeUse Use {
            get { return m_use; }
        }
        public XsType Type {
            get { return m_type; }
        }
        public object Default {
            get { return m_default; }
        }

        // any attribute
        public XsProcessContents ProcessContents {
            get { return m_processContents; }
        }
        public IEnumerable<XNamespace> Namespaces() {
            return m_namespaces;
        }
        public XsNamespaceType NamespaceType {
            get { return m_namespaceType; }
        }
    }

    public abstract class XsComplexContent : XsObject {
        internal static XsSchema GetDeclaringSchema(object parent) {
            var schema = parent as XsSchema;
            if (schema == null) {

                var type = GetDeclaringType(parent);
                if (type == null)
                    return null;

                schema = type.Schema;
            }

            return schema;
        }
        internal static XsType GetDeclaringType(object parent) {
            var type = parent as XsType;
            if (type == null) {

                var content = parent as XsComplexContent;
                if (content == null)
                    return null;

                type = content.DeclaringType;
            }

            return type;
        }

        private readonly XsSchema m_schema;
        private readonly XsType m_declaringType;
        private readonly XsComplexContent m_declaringContent;
        private readonly int m_minOccurs;
        private readonly int m_maxOccurs;
        private readonly Lazy<IEnumerable<XsComplexContent>> m_content;

        #region Constructor
        internal XsComplexContent(
            object parent,
            int minOccurs,
            int maxOccurs,
            Func<XsComplexContentFactory, IEnumerable<XsComplexContent>> contentFactory = null,

            XsMetadataFactory metadata = null
        ) : base(
            loader: GetDeclaringSchema(parent).Loader, 
            metadata: metadata
        ) {
            m_minOccurs = minOccurs;
            m_maxOccurs = maxOccurs;

            m_schema = GetDeclaringSchema(parent);
            m_declaringType = GetDeclaringType(parent);
            m_declaringContent = parent as XsComplexContent;

            m_content = new Lazy<IEnumerable<XsComplexContent>>(() => {
                if (contentFactory == null)
                    return Enumerable.Empty<XsComplexContent>();

                return contentFactory(new XsComplexContentFactory(this)).ToList();
            });
        }
        #endregion

        public XsSchema Schema {
            get { return m_schema; }
        }
        public XsComplexContent DeclaringContent {
            get { return m_declaringContent; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        public int MinOccurs {
            get { return m_minOccurs; }
        }
        public int MaxOccurs {
            get { return m_maxOccurs; }
        }
        public IEnumerable<XsComplexContent> Content() {
            return m_content.Value;
        }
    }
    public sealed class XsElement : XsComplexContent {
        public const int DefaultMinOccurs = 1;
        public const int DefaultMaxOccurs = 1;

        private readonly XsType m_type;
        private readonly object m_default;
        private readonly string m_name;
        private readonly XsForm m_form;

        #region Constructor
        internal XsElement(
            object parent,

            string name,
            XsForm form,
            XsType type,
            object @default,

            int minOccurs,
            int maxOccurs,

            XsMetadataFactory metadata = null
        ) : base(
            parent: parent,

            minOccurs: minOccurs,
            maxOccurs: maxOccurs,

            metadata: metadata
        ) {
            if (form == XsForm.Undefined)
                form = Schema.ElementFormDefault;

            m_name = name;
            m_form = form;
            m_type = type;
            m_default = @default;
        }
        #endregion

        public string Name {
            get { return m_name; }
        }
        public XName XName {
            get {
                var xNamespace = Form == XsForm.Unqualified ? XNamespace.None : Schema.XNamespace;
                var xName = xNamespace + Name;
                return xName;
            }
        }

        public XsForm Form {
            get { return m_form; }
        }
        public object Default {
            get { return m_default; }
        }
        public XsType Type {
            get { return m_type; }
        }
    }
    public sealed class XsAll : XsComplexContent {

        #region Constructor
        internal XsAll(
            object parent,
            Func<XsComplexContentFactory, IEnumerable<XsElement>> contentFactory,

            XsMetadataFactory metadata = null
        ) : base(
            parent: parent, 

            minOccurs: 1,
            maxOccurs: 1,
            contentFactory: contentFactory,

            metadata: metadata
        ) { 
        }
        #endregion
    }
    public sealed class XsChoice : XsComplexContent {

        #region Constructor
        internal XsChoice(
            object parent,

            int minOccurs,
            int maxOccurs,
            Func<XsComplexContentFactory, IEnumerable<XsComplexContent>> contentFactory,

            XsMetadataFactory metadata = null
        ) : base(
            parent: parent,

            minOccurs: minOccurs,
            maxOccurs: maxOccurs,
            contentFactory: contentFactory,

            metadata: metadata
        ) {
        }
        #endregion
    }
    public sealed class XsSequence : XsComplexContent {

        #region Constructor
        internal XsSequence(
            object parent,

            int minOccurs,
            int maxOccurs,
            Func<XsComplexContentFactory, IEnumerable<XsComplexContent>> contentFactory,

            XsMetadataFactory metadata = null
        ) : base(
            parent: parent,

            minOccurs: minOccurs,
            maxOccurs: maxOccurs,
            contentFactory: contentFactory,

            metadata: metadata
        ) {
        }
        #endregion
    }
    public sealed class XsAny : XsComplexContent {
        private readonly IEnumerable<XNamespace> m_namespaces;
        private readonly XsProcessContents m_processContents;
        private readonly XsNamespaceType m_type;

        #region Constructor
        internal XsAny(
            object parent,

            int minOccurs,
            int maxOccurs,
            XsProcessContents processContents,
            IEnumerable<XNamespace> namespaces,
            XsNamespaceType type,

            XsMetadataFactory metadata = null
        )
            : base(
            parent: parent, 

            minOccurs: minOccurs,
            maxOccurs: maxOccurs,

                metadata: metadata
        ) {

            m_namespaces = namespaces ?? Enumerable.Empty<XNamespace>();
            m_type = type;
            m_processContents = processContents;
        }
        #endregion

        public XsProcessContents ProcessContents {
            get { return m_processContents; }
        }
        public IEnumerable<XNamespace> Namespaces() {
            return m_namespaces;
        }
        public XsNamespaceType Type {
            get { return m_type; }
        }
    }

    public class XQName {
        public static explicit operator XQName(XAttribute attribute) {
            return (XQName)(string)attribute;
        }
        public static implicit operator XQName(string attribute) {
            var split = attribute.Split(':');
            if (split.Length == 1)
                return new XQName(split[0]);

            return new XQName(split[1], split[0]);
        }

        private XPrefix m_xPrefix;
        private string m_localName;

        public XQName(string localName, XPrefix xPrefix = null) {
            m_xPrefix = xPrefix ?? XPrefix.None;
            m_localName = localName;
        }

        public XPrefix XPrefix {
            get { return m_xPrefix; }
        }
        public string LocalName {
            get { return m_localName; }
        }

        public override string ToString() {
            if (m_xPrefix == null || m_xPrefix == XPrefix.None)
                return m_localName;

            return m_xPrefix.ToString() + ":" + m_localName;
        }
        public override int GetHashCode() {
            return ToString().GetHashCode();
        }
        public override bool Equals(object obj) {
            var qName = obj as XQName;
            if (qName == null)
                return false;

            return ToString().Equals(qName.ToString());
        }
    }
    public class XPrefix {
        public static readonly XPrefix None = new XPrefix(null);

        public static bool operator ==(XPrefix lhs, XPrefix rhs) {
            if ((object)lhs == null && (object)rhs == null)
                return true;

            if ((object)lhs == null || (object)rhs == null)
                return false;

            return lhs.Equals(rhs);
        }
        public static bool operator !=(XPrefix lhs, XPrefix rhs) {
            return !(lhs == rhs);
        }
        public static implicit operator string(XPrefix prefix) {
            if (prefix == null)
                return null;
            return prefix.m_value;
        }
        public static implicit operator XPrefix(string prefix) {
            if (prefix == null)
                return null;
            return new XPrefix(prefix);
        }
        public static XQName operator+(XPrefix prefix, string localName) {
            return new XQName(localName, prefix);
        }

        private readonly string m_value;

        public XPrefix(string value) {
            m_value = value ?? string.Empty;
        }

        public string Value {
            get { return m_value; }
        }

        public override bool Equals(object obj) {
            var rhs = this;
            var lhs = obj as XPrefix;

            if (lhs == null)
                return false;

            return lhs.m_value == rhs.m_value;
        }
        public override int GetHashCode() {
            return m_value.GetHashCode();
        }
        public override string ToString() {
            return m_value;
        }
    }

    public abstract class XsFacet {

        #region Applicability
        internal const XsFacetType UniversalFacets = XsFacetType.Pattern | XsFacetType.Assertions;
        internal const XsFacetType UniversalAtomicFacets = UniversalFacets | XsFacetType.Whitespace;
        // all but Boolean and Union share these facets
        internal const XsFacetType GeneralFacets = UniversalAtomicFacets | XsFacetType.Enumeration;

        internal const XsFacetType CardinalityFacets = XsFacetType.Cardinality | XsFacetType.MaximumCardinality | XsFacetType.MinimumCardinality;
        internal const XsFacetType LengthFacets = XsFacetType.Length | XsFacetType.MaximumLength | XsFacetType.MinimumLength;

        internal const XsFacetType DigitFacets = XsFacetType.TotalDigits | XsFacetType.FractionDigits;
        internal const XsFacetType RangeFacets =
            XsFacetType.MinimumInclusive | XsFacetType.MaximumInclusive | XsFacetType.MinimumExclusive | XsFacetType.MaximumExclusive;

        // string, hexBinary, base64Binary, anyUri, qName, notation
        internal const XsFacetType StringFacets = GeneralFacets | LengthFacets;
        internal const XsFacetType BooleanFacets = UniversalAtomicFacets;

        internal const XsFacetType DurationFacets = GeneralFacets | RangeFacets;
        // dateTime, time, date, yearMonth, year, monthDay, day, month
        internal const XsFacetType TimeLineFacets = DurationFacets | XsFacetType.ExplicitTimeZone;

        // float, decimal
        internal const XsFacetType NumericalFacets = GeneralFacets | RangeFacets;
        internal const XsFacetType DecimalFacets = NumericalFacets | DigitFacets;

        internal const XsFacetType ListRestrictionFacets = GeneralFacets | CardinalityFacets;
        internal const XsFacetType UnionRestrictionFacets = UniversalFacets | XsFacetType.Enumeration;
        #endregion

        #region Facets
        internal sealed class Enumeration : XsFacet {
            private Lazy<HashSet<object>> m_values;

            internal Enumeration(XsType type, IEnumerable<object> values)
                : base(type, values, false) {

                m_values = new Lazy<HashSet<object>>(() => values.ToHashSet());
            }

            protected override bool TryTestValue(object value) {

                bool result = false;

                // enumeration on subclass of list
                if (value is IEnumerable) {

                    var valueList = value.ToEnumerable();
                    foreach (var o in m_values.Value.ToEnumerable()) {

                        var enumerationList = o.ToEnumerable();
                        if (Enumerable.SequenceEqual(enumerationList, valueList))
                            return true;
                    };
                }

                else {
                    // enumeration on subclass of union
                    if (value is XsTypedValue)
                        value = ((XsTypedValue)value).Value;

                    result = m_values.Value.Contains(value);
                }

                return result;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                throw new InvalidOperationException();
            }
        }
        internal sealed class Pattern : XsFacet {
            internal Pattern(XsType type, params string[] patterns)
                : base(type, patterns, false) {
            }

            protected override bool TryTestLiteral(string literal) {               
                foreach (var pattern in (string[])Value) {
                    if (Regex.IsMatch(literal, pattern))
                        return true;
                }

                return false;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                throw new InvalidOperationException();
            }
        }

        internal sealed class Cardinality : XsFacet {
            internal Cardinality(
                XsType type,
                object length,
                bool isFixed)
                : base(type, length, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                var enumerable = value as IEnumerable;
                return enumerable.Cast<object>().Count() == Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value == constrainedValue;
            }
        }
        internal sealed class MaximumCardinality : XsFacet {
            internal MaximumCardinality(
                XsType type,
                object maximumCardinality,
                bool isFixed)
                : base(type, maximumCardinality, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                var enumerable = value as IEnumerable;
                return enumerable.Cast<object>().Count() <= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }
        internal sealed class MinimumCardinality : XsFacet {
            internal MinimumCardinality(
                XsType type,
                object minimumCardinality,
                bool isFixed)
                : base(type, minimumCardinality, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                var enumerable = value as IEnumerable;
                return enumerable.Cast<object>().Count() >= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value >= constrainedValue;
            }
        }

        internal sealed class Length : XsFacet {
            internal Length
                (XsType type,
                object length,
                bool isFixed)
                : base(type, length, isFixed) {
            }

            protected override bool TryTestLiteral(string normalizedLiteral) {
                return normalizedLiteral.Length == Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value == constrainedValue;
            }
        }
        internal sealed class MaximumLength : XsFacet {
            internal MaximumLength(
                XsType type,
                object maximumLength,
                bool isFixed)
                : base(type, maximumLength, isFixed) {
            }

            protected override bool TryTestLiteral(string normalizedLiteral) {
                return normalizedLiteral.Length <= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }
        internal sealed class MinimumLength : XsFacet {
            internal MinimumLength(
                XsType type,
                object minimumLength,
                bool isFixed)
                : base(type, minimumLength, isFixed) {
            }

            protected override bool TryTestLiteral(string normalizedLiteral) {
                return normalizedLiteral.Length >= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value >= constrainedValue;
            }
        }

        internal sealed class MinimumInclusive : XsFacet {
            internal MinimumInclusive(
                XsType type,
                object minimumInclusive,
                bool isFixed)
                : base(type, minimumInclusive, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                return value >= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value >= constrainedValue;
            }
        }
        internal sealed class MaximumInclusive : XsFacet {
            internal MaximumInclusive(
                XsType type,
                object maximumInclusive,
                bool isFixed)
                : base(type, maximumInclusive, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                return value <= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }
        internal sealed class MinimumExclusive : XsFacet {
            internal MinimumExclusive(
                XsType type,
                object minimumExclusive,
                bool isFixed)
                : base(type, minimumExclusive, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                return value > Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value >= constrainedValue;
            }
        }
        internal sealed class MaximumExclusive : XsFacet {
            internal MaximumExclusive(
                XsType type,
                object maximumExclusive,
                bool isFixed)
                : base(type, maximumExclusive, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                return value < Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }

        internal sealed class TotalDigits : XsFacet {
            internal TotalDigits(
                XsType type,
                object totalDigits,
                bool isFixed)
                : base(type, totalDigits, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                var literal = (string)value.ToString();
                literal = literal.Trim('0', '-', '+');

                var digits = literal.Replace(".", null).Length;
                return digits <= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }
        internal sealed class FractionDigits : XsFacet {
            internal FractionDigits(
                XsType type,
                object fractionDigits,
                bool isFixed)
                : base(type, fractionDigits, isFixed) {
            }

            protected override bool TryTestValue(dynamic value) {
                var literal = (string)value.ToString();
                var fractionalDigits = 0;

                var match = Regex.Match(literal, @"\d*[.](?<fractionalDigits>\d*)");
                if (match.Success)
                    fractionalDigits = match.Get("fractionalDigits").Length;

                return fractionalDigits <= Value;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                return Value <= constrainedValue;
            }
        }
        internal sealed class Whitespace : XsFacet {
            internal Whitespace(
                XsType type,
                XsWhitespaceFacetType whiteSpace,
                bool isFixed)
                : base(type, whiteSpace, isFixed) {
            }

            protected override bool TryTestLiteral(string normalizedLiteral) {
                return true;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                if ((Value == XsWhitespaceFacetType.Replace || Value == XsWhitespaceFacetType.Preserve) &&
                    constrainedValue == XsWhitespaceFacetType.Collapse)
                    return false;

                if (Value == XsWhitespaceFacetType.Preserve && constrainedValue == XsWhitespaceFacetType.Replace)
                    return false;

                return true;
            }
        }
        internal sealed class ExplicitTimeZone : XsFacet {
            internal ExplicitTimeZone(
                XsType type,
                XsExplicitTimezoneFacetType explicitTimeZone,
                bool isFixed)
                : base(type, explicitTimeZone, isFixed) {
            }

            protected override bool TryTestLiteral(string normalizedLiteral) {
                var explicitTimezone = (XsExplicitTimezoneFacetType)Value;
                if (normalizedLiteral == null || explicitTimezone == XsExplicitTimezoneFacetType.Optional)
                    return true;

                var expected = explicitTimezone == XsExplicitTimezoneFacetType.Required;
                var result = Regex.IsMatch(normalizedLiteral, @"Z|([+|-]\d\d:\d\d)$") == expected;
                return result;
            }
            protected override bool TestConstraint(dynamic constrainedValue) {
                if (Value == XsExplicitTimezoneFacetType.Optional)
                    return true;

                return Value == constrainedValue;
            }
        }
        #endregion

        private readonly object m_value;
        private readonly bool m_isFixed;
        private readonly XsType m_declaringType;
        private readonly XsFacet m_constrainedFacet;
        private readonly XsFacetType m_facetType;

        protected XsFacet(XsType declaringType, object value, bool isFixed) {
            m_declaringType = declaringType;
            m_isFixed = isFixed;
            m_facetType = (XsFacetType)Enum.Parse(typeof(XsFacetType), GetType().Name);

            if (!declaringType.ApplicableFacets.HasFlag(FacetType))
                throw new XsLoaderException(
                    "Facet {0} cannot be applied to type {1}."
                    .Substitute(FacetType, m_declaringType));

            m_value = value;

            // patterns do not constrain
            if (this is Pattern)
                return;

            m_constrainedFacet = declaringType.BaseType.GetFacet(FacetType);

            if (m_constrainedFacet != null) {

                if (m_constrainedFacet.IsFixed)
                    throw new XsLoaderException(
                       ("Facet {0} declared on type '{1}' cannot restrict the " +
                        "same type of facet declared on base class '{2}' because the latter is fixed."
                        ).Substitute(FacetType, m_declaringType, m_constrainedFacet.DeclaringType)
                    );

                if (!TestConstraint(m_constrainedFacet.Value)) {
                    throw new XsLoaderException(
                       ("Facet {0} declared on type '{1}' has a value '{2}' which fails to restrict "
                        + "the same facet declared on base class '{3}' with the value '{4}'."
                       ).Substitute(FacetType, m_declaringType, (object)value, m_constrainedFacet.DeclaringType, (object)m_constrainedFacet.Value)
                    );
                }
            }
        }

        protected virtual bool TryTestValue(dynamic value) {
            return true;
        }
        protected virtual bool TryTestLiteral(string literal) {
            return true;
        }
        protected abstract bool TestConstraint(dynamic constrainedValue);

        internal bool TestValue(dynamic value, bool throwOnError) {
            if (TryTestValue(value))
                return true;

            if (!throwOnError)
                return false;

            if (this is Enumeration) {
                throw new XsSerializationException(
                    "Value '{0}' failed facet '{1}' declared on '{2}' for all enumerations: {3}."
                    .Substitute((object)value, m_facetType, m_declaringType,
                        new TextJoin("', '", "'", "'") { Value }.ReadToEnd()
                    )
                );
            }

            throw new ArgumentException(
                "Value '{0}' failed facet '{1}' with value '{2}' declared on '{3}'."
                .Substitute((object)value, m_facetType, (object)Value, m_declaringType));
        }
        internal bool TestLiteral(string literal, bool throwOnError) {
            if (TryTestLiteral(literal))
                return true;

            if (!throwOnError)
                return false;

            if (this is Pattern) {
                throw new XsSerializationException(
                    "Literal '{0}' failed facet '{1}' declared on '{2}' for all patterns: {3}."
                    .Substitute(literal, m_facetType, m_declaringType, 
                        new TextJoin(", ", "'", "'") { Value }.ReadToEnd()
                    )
                );
            }

            throw new XsSerializationException(
                "Literal '{0}' failed facet '{1}' with value '{2}' declared on '{3}'."
                .Substitute(literal, m_facetType, (object)Value, m_declaringType));
        }

        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        public XsFacetType FacetType {
            get { return m_facetType; }
        }
        public XsFacet ConstrainedFacet {
            get { return m_constrainedFacet; }
        }
        public dynamic Value { 
            get { return m_value; } 
        }
        public bool IsFixed {
            get { return m_isFixed; }
        }

        public override string ToString() {
            return GetType().Name;
        }
    }
    public sealed class XsTypedValue {
        public static explicit operator string(XsTypedValue value) {
            return (string)value.Value;
        }
        public static explicit operator bool(XsTypedValue value) {
            return (bool)value.Value;
        }

        public static explicit operator byte[](XsTypedValue value) {
            return (byte[])value.Value;
        }
        public static explicit operator Uri(XsTypedValue value) {
            return (Uri)value.Value;
        }
        public static explicit operator XsDateTime(XsTypedValue value) {
            return (XsDateTime)value.Value;
        }
        public static explicit operator XName(XsTypedValue value) {
            return (XName)value.Value;
        }

        public static explicit operator float(XsTypedValue value) {
            return (float)value.Value;
        }
        public static explicit operator double(XsTypedValue value) {
            return (double)value.Value;
        }

        public static explicit operator sbyte(XsTypedValue value) {
            return (sbyte)value.Value;
        }
        public static explicit operator short(XsTypedValue value) {
            return (short)value.Value;
        }
        public static explicit operator int(XsTypedValue value) {
            return (int)value.Value;
        }
        public static explicit operator long(XsTypedValue value) {
            return (long)value.Value;
        }
        public static explicit operator byte(XsTypedValue value) {
            return (byte)value.Value;
        }
        public static explicit operator ushort(XsTypedValue value) {
            return (ushort)value.Value;
        }
        public static explicit operator uint(XsTypedValue value) {
            return (uint)value.Value;
        }
        public static explicit operator ulong(XsTypedValue value) {
            return (ulong)value.Value;
        }

        private readonly XsType m_type;
        private readonly object m_value;

        internal XsTypedValue(XsType type, object value) {
            if (value == null)
                throw new ArgumentNullException();

            m_type = type;
            m_value = value;
        }

        public XsType Type {
            get { return m_type; }
        }
        public object Value {
            get { return m_value; }
        }

        public override bool Equals(object obj) {
            if (!(obj is XsTypedValue))
                return false;

            var rhs = (XsTypedValue)obj;

            return m_type == rhs.m_type && object.Equals(m_value, rhs.m_value);
        }
        public override int GetHashCode() {
            if (m_value == null)
                return m_type.GetHashCode();

            return m_value.GetHashCode();
        }
        public override string ToString() {
            return "{{{0}}} '{1}'".Substitute(Type, (object)Value);
        }
    }
}
namespace King.Xml.Reflection.Emit {

    public static class XsResolver {

        public static string ResolveName(
            object key,
            XNamespace xNamespace,
            Func<string, string> toCSharpName = null,
            Func<object, XName> resolveXName = null) {

            if (toCSharpName == null)
                toCSharpName = o => o;

            // name
            var name = key as string;
            if (name == null) {

                // xName
                var xName = key as XName;
                if (xName == null) {

                    if (resolveXName == null)
                        return null;

                    xName = resolveXName(key);
                    if (xName == null)
                        return null;
                }

                // verify XNamespace
                if (xName.Namespace != xNamespace)
                    return null;

                // convert to CSharpName
                name = toCSharpName(xName.LocalName);
            }

            return name;
        }
    }

    internal sealed partial class XsXDocumentFactory {
        internal static XDocument ToXsd(XsSchema xsSchema) {
            var xs = XsLoader.XsdXNamespace;

            var doc = new XDocument(
                new XDeclaration("1.0", "uft-8", "yes"),
                new XElement(XsdSchema,
                    // targetNamespace
                    xsSchema.XNamespace == XNamespace.None ? null :
                        new XAttribute(TargetNamespace, xsSchema.XNamespace),

                    // elementFormDefault
                    new XAttribute(ElementFormDefault, "qualified"),

                    // prefixes
                    xsSchema.Prefixes().Select(o => {
                        var xNamespace = xsSchema.GetSchema(o).XNamespace;
                        ASSERT.Condition(xNamespace != XNamespace.None);

                        var xName = (XName)"xmlns";
                        if (o != XPrefix.None)
                            xName = XNamespace.Xmlns + o;

                        return new XAttribute(xName, xNamespace);
                    }),

                    xsSchema.RootElements().Select(o => o.ToXsd()),
                    xsSchema.AttributeGroups().Select(o => o.ToXsd()),
                    xsSchema.Types().Select(o => o.ToXsd())
                )
            );

            return doc;
        }
        internal static XObject ToXsd(XsType xsType) {
            if (xsType.IsSimpleType)
                return ToXsdSimpleType(xsType);

            return ToXsdComplexType(xsType);
        }
        internal static XObject ToXsd(XsElement xsElement) {

            return new XElement(XsdElement,
                new XAttribute("name", xsElement.Name),
                new XAttribute("type", xsElement.Schema.GetQName(xsElement.Type)),

                xsElement.MinOccurs == XsElement.DefaultMinOccurs ? 
                    null : new XAttribute("minOccurs", xsElement.MinOccurs),

                xsElement.MaxOccurs == XsElement.DefaultMaxOccurs ? 
                    null : new XAttribute("maxOccurs", xsElement.MaxOccurs)
            );
        }

        private static XObject ToXsdComplexType(XsType xsType) {
            return new XElement(XsdComplexType,
                new XAttribute("name", xsType.XName.LocalName)
            );
        }
        private static XObject ToXsdSimpleType(XsType xsType) {
            return new XElement(XsdSimpleType,
                new XAttribute("name", xsType.XName.LocalName)
            );
        }
    }
    internal sealed partial class XsXDocumentFactory {

        private static readonly XNamespace Xsd = XsLoader.XsdXNamespace;
        private static readonly XName XsdSchema = Xsd + "schema";
        private static readonly XName TargetNamespace = "targetNamespace";
        private static readonly XName ElementFormDefault = "elementFormDefault";

        private static readonly XName XsdComplexType = Xsd + "complexType";
        private static readonly XName XsdComplexContent = Xsd + "complexContent";
        private static readonly XName XsdSimpleType = Xsd + "simpleType";
        private static readonly XName XsdAttributeGroup = Xsd + "attributeGroup";
        private static readonly XName XsdElement = Xsd + "element";
        private static readonly XName XsdRestriction = Xsd + "restriction";
        private static readonly XName XsdExtension = Xsd + "extension";
        private static readonly XName XsdAttribute = Xsd + "attribute";

        private static readonly XName XsdSequence = Xsd + "sequence";
        private static readonly XName XsdChoice = Xsd + "choice";
        private static readonly XName XsdAny = Xsd + "any";
        private static readonly XName XsdAll = Xsd + "all";

        internal static XsSchema CreateSchema(XsSchemaFactory schemaFactory, XDocument xDocument) {
            var tns = schemaFactory.XNamespace;
            var xRoot = xDocument.Root;

            // prefix -> namespace
            var xNamespaceByXPrefix =
               (from o in xRoot.Attributes()
                let localName = o.Name.LocalName
                let ns = o.Name.Namespace
                let isDefaultNs = ns == XNamespace.None && localName == "xmlns"
                where ns == XNamespace.Xmlns || isDefaultNs
                select new {
                    Prefix = isDefaultNs ? XPrefix.None : (XPrefix)localName,
                    XNamespace = (XNamespace)o.Value
                }).ToDictionary(o => o.Prefix, o => o.XNamespace);

            // gather types
            var complexTypes = xRoot.Elements(XsdComplexType);
            var simpleTypes = xRoot.Elements(XsdSimpleType);
            var types = complexTypes.Union(simpleTypes).ToDictionary(o => tns + (string)o.Attribute("name"));

            // generate schemas
            return schemaFactory.CreateSchema(
                xNamespaceByXPrefix: new Lazy<Dictionary<XPrefix, XNamespace>>(() => xNamespaceByXPrefix), 

                typeNames: o => types.Keys,
                typeFactory: o => CreateType(o, types),

                attributeGroupNames: o => xRoot.Elements(XsdAttributeGroup).Select(x => x.Attribute("name")),
                attributeGroupFactory: o => null,

                rootElementNames: o => xRoot.Elements(XsdElement).Select(x => x.Attribute("name")),
                rootElementFactory: o => null
            );
        }

        internal static XsType CreateType(XsTypeFactory factory, Dictionary<XName, XElement> types) {
            var xElement = types.GetValueOrDefault(factory.XName);
            if (xElement == null)
                return null;

            if (xElement.Name == XsdComplexType)
                return CreateComplexType(factory, xElement);

            if (xElement.Name == XsdSimpleType)
                return CreateSimpleType(factory, xElement);

            throw new NotSupportedException();
        }
        internal static XsType CreateComplexType(XsTypeFactory factory, XElement xComplexType) {
            var contentRoot = xComplexType.Elements().First();
            
            var baseType = factory.Loader.AnyType;
            var isRestriction = true;

            var complexContent = xComplexType.Element(XsdComplexContent);
            if (complexContent != null) {
                var restriction = complexContent.Element(XsdRestriction);
                var extension = complexContent.Element(XsdExtension);
                contentRoot = restriction ?? extension;

                isRestriction = restriction != null;

                baseType = factory.Schema.GetType((XQName)contentRoot.Attribute("base"));
            }

            return factory.CreateComplexTypeWithComplexContent(
                baseType: baseType,

                contentFactory: o => CreateContent(o, contentRoot),
                attributeFactory: o => CreateAttributes(o, contentRoot),

                isRestriction: isRestriction,
                isAbstract: (bool?)contentRoot.Attribute("abstract") ?? false,
                //isFinalExtension: (bool?)contentRoot.Attribute("isFinalExtension") ?? false,
                //isFinalRestriction: (bool?)contentRoot.Attribute("isFinalRestriction") ?? false,
                isMixed: (bool?)contentRoot.Attribute("isMixed") ?? false
            );
        }
        internal static IEnumerable<object> CreateAttributes(XsAttributeFactory factory, XElement contentRoot) {
            var schema = factory.Schema;

            // attribute
            foreach (var attribute in contentRoot.Elements(XsdAttribute)) {
                XsAttributeUse use;
                Enum.TryParse<XsAttributeUse>((string)attribute.Attribute("use"), out use);

                var type = schema.GetType((XQName)attribute.Attribute("type"));
                var @default = type.Deserialize((string)attribute.Attribute("default"));

                yield return factory.CreateAttribute(
                    name: (string)attribute.Attribute("name"), 
                    xsType: type,
                    use: use,
                    @default: @default
                );
            }

            // attribute group
            foreach (var attributeGroup in contentRoot.Elements(XsdAttributeGroup)) {
                yield return factory.CreateAttributeGroup();
            }
        }
        internal static XsComplexContent CreateContent(XsComplexContentFactory factory, XElement contentRoot) {

            var group = contentRoot.Elements().First();
            if (group.Name == XsdSequence) {
            }

            if (group.Name == XsdChoice) {
            }

            if (group.Name == XsdAny) {
            }

            if (group.Name == XsdAll) {
            }

            throw new NotSupportedException();
        }
        internal static XsType CreateSimpleType(XsTypeFactory factory, XElement xSimpleType) {
            var loader = factory.Loader;
            var schema = factory.Schema;
            var restriction = xSimpleType.Element(XsdRestriction);
            var baseType = schema.GetType((XQName)restriction.Attribute("base"));

            return factory.CreateSimpleType(baseType);
        }
    }

    public sealed class XsSchemaFactory {
        private XsLoader m_loader;
        private XNamespace m_xNamespace;

        internal XsSchemaFactory(XsLoader loader, XNamespace xNamespace) {
            m_loader = loader;
            m_xNamespace = xNamespace;
        }

        public XsLoader Loader {
            get { return m_loader; }
        }
        public XNamespace XNamespace {
            get { return m_xNamespace; }
        }

        public XsSchema CreateSchema(XDocument xDocument) {
            return XsXDocumentFactory.CreateSchema(this, xDocument);
        }
        public XsSchema CreateSchema(
            Lazy<Dictionary<XPrefix, XNamespace>> xNamespaceByXPrefix,
            Func<XsSchema, IEnumerable<object>> typeNames = null,
            Func<XsTypeFactory, XsType> typeFactory = null,
            Func<XsSchema, IEnumerable<object>> attributeGroupNames = null,
            Func<XsAttributeGroupFactory, XsAttributeGroup> attributeGroupFactory = null,
            Func<XsSchema, IEnumerable<object>> rootElementNames = null,
            Func<XsRootElementFactory, XsElement> rootElementFactory = null,
            XsForm attributeDefaultForm = XsForm.Unqualified,
            XsForm elementDefaultForm = XsForm.Unqualified,
            XsMetadataFactory metadata = null
        ) {
            if (typeNames == null)
                typeNames = o => Enumerable.Empty<object>();

            if (typeFactory == null)
                typeFactory = o => null;

            if (attributeGroupNames == null)
                attributeGroupNames = o => Enumerable.Empty<object>();

            if (attributeGroupFactory == null)
                attributeGroupFactory = o => null;

            if (rootElementNames == null)
                rootElementNames = o => Enumerable.Empty<object>();

            if (rootElementFactory == null)
                rootElementFactory = o => null;

            return new XsSchema(
                xNamespaceByXPrefix: xNamespaceByXPrefix,
                loader: m_loader, 
                ns: XNamespace,
                attributeFormDefault: attributeDefaultForm,
                elementFormDefault: elementDefaultForm,
                typeNames: typeNames, 
                typeFactory: typeFactory,
                attributeGroupNames: attributeGroupNames,
                attributeGroupFactory: attributeGroupFactory,
                rootElementNames: rootElementNames,
                rootElementFactory: rootElementFactory,
                metadata: metadata
            );
        }
    }
    public sealed class XsTypeFactory {
        private XsSchema m_schema;
        private XName m_xName;
        private XsType m_declaringType;

        internal XsTypeFactory(XsSchema schema, XsType declaringType) {
            m_schema = schema;
            m_declaringType = declaringType;
        }
        internal XsTypeFactory(XsSchema schema, XName xName) {
            m_schema = schema;
            m_xName = xName;
        }

        public XsLoader Loader {
            get { return m_schema.Loader; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public XName XName {
            get { return m_xName; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }

        internal XsType DefineBuiltInSimpleType(
            XsType baseType,
            Type clrType = null,
            Func<XsFacetFactory, IEnumerable<XsFacet>> facets = null,
            Func<string, bool, object> parseLiteral = null,
            XsMetadataFactory metadata = null
        ) {

            if (baseType == null)
                throw new ArgumentNullException();

            if (facets == null)
                facets = o => Enumerable.Empty<XsFacet>();

            return new XsBuiltInSimpleType(
                schema: m_schema,
                xName: m_xName,
                clrType: clrType,
                baseType: baseType,
                getFacets: facets,
                parseLiteral: parseLiteral,
                metadata: metadata
            );
        }

        public XsType CreateComplexTypeWithSimpleContent(
            XsType baseType,
            Func<XsTypeFactory, XsType> simpleContent,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,
            bool isAbstract = false,
            bool isRestriction = false,
            XsFinal final = XsFinal.None,
            XsMetadataFactory metadata = null
        ) {

            return new XsComplexTypeWithSimpleContent(
                schema: Schema,
                xName: XName,
                declaringType: DeclaringType,
                baseType: baseType,
                isRestriction: isRestriction,
                isAbstract: isAbstract,
                final: final,
                attributeFactory: attributeFactory,
                simpleContent: simpleContent,
                metadata: metadata
            );
        }

        public XsType CreateComplexTypeWithComplexContent(
            XsType baseType,
            Func<XsComplexContentFactory, XsComplexContent> contentFactory,
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,
            bool isRestriction = false,
            bool isAbstract = false,
            bool isMixed = false,
            XsFinal final = XsFinal.None,
            XsMetadataFactory metadata = null
        ) {

            return new XsComplexTypeWithComplexContent(
                schema: Schema,
                xName: XName,
                declaringType: DeclaringType,
                baseType: baseType,
                isRestriction: isRestriction,
                isAbstract: isAbstract,
                isMixed: isMixed,
                final: final,
                complexContentFactory: contentFactory,
                attributeFactory: attributeFactory,
                metadata: metadata
            );
        }
        
        public XsType CreateSimpleType(
            XsType baseType,
            Func<XsFacetFactory, IEnumerable<XsFacet>> facets = null,
            XsMetadataFactory metadata = null
        ) {
            
            if (baseType == null)
                throw new ArgumentNullException();

            if (facets == null)
                facets = o => Enumerable.Empty<XsFacet>();

            return new XsConstrainedSimpleType(
                schema: m_schema,
                xName: m_xName,
                baseType: baseType,
                getFacets: facets,
                metadata: metadata
            );
        }

        public XsType CreateListType(
            XsType listType,
            XsMetadataFactory metadata = null
        ) {
            if (listType == null)
                throw new ArgumentNullException();

            return new XsListSimpleType(
                schema: m_schema, 
                xName: m_xName,
                listType: listType,
                metadata: metadata
            );
        }

        public XsType CreateUnionType(
            IEnumerable<XsType> unionTypes,
            XsMetadataFactory metadata = null
        ) {

            return new XsUnionSimpleType(
                schema: m_schema, 
                xName: m_xName,
                unionTypes: unionTypes.ToArray(),
                metadata: metadata
            );
        }
    }
    public sealed class XsAttributeGroupFactory {
        private XsSchema m_schema;
        private XName m_xName;

        internal XsAttributeGroupFactory(
            XsSchema schema,
            XName xName) {
            m_schema = schema;
            m_xName = xName;
        }

        public XsLoader Loader {
            get { return m_schema.Loader; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public XName XName {
            get { return m_xName; }
        }

        public XsAttributeGroup CreateAttributeGroup(
            Func<XsAttributeFactory, IEnumerable<object>> attributeFactory,
            XsMetadataFactory metadata = null
        ) {
            
            return new XsAttributeGroup(
                schema: Schema,
                xName: XName,
                attributeFactory: attributeFactory,
                metadata: metadata
            );
        }
    }
    public sealed class XsAttributeFactory {
        private XsSchema m_schema;
        private XsType m_declaringType;
        private XsAttributeGroup m_declaringAttributeGroup;

        internal XsAttributeFactory(XsSchema schema, XsType declaringType) {
            m_schema = schema;
            m_declaringType = declaringType;
        }
        internal XsAttributeFactory(XsSchema schema, XsAttributeGroup declaringAttributeGroup) {
            m_schema = schema;
            m_declaringAttributeGroup = declaringAttributeGroup;
        }

        public XsLoader Loader {
            get { return Schema.Loader; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public XsType DeclaringType {
            get { return m_declaringType; }
        }
        public XsAttributeGroup DeclaringAttributeGroup {
            get { return m_declaringAttributeGroup; }
        }

        public XsAttribute CreateAttribute(
            string name,
            XsType xsType,
            XsForm form = XsForm.Undefined,
            XsAttributeUse use = XsAttributeUse.Required,
            object @default = null,
            XsMetadataFactory metadata = null
        ) {

            return new XsAttribute(
                schema: Schema,
                declaringType: DeclaringType,
                declaringAttributeGroup: DeclaringAttributeGroup,

                name: name,
                form: form,
                type: xsType,
                use: use,
                @default: @default,

                metadata: metadata
            );
        }

        public XsAttribute CreateAnyAttribute(
            XsProcessContents processContents = XsProcessContents.Strict,
            IEnumerable<XNamespace> namespaces = null,
            XsNamespaceType namespaceType = XsNamespaceType.None,
            XsMetadataFactory metadata = null
        ) {

            return new XsAttribute(
                schema: Schema,
                declaringType: DeclaringType,
                declaringAttributeGroup: DeclaringAttributeGroup,

                processContents: processContents,
                namespaces: namespaces,
                namespaceType: namespaceType,

                metadata: metadata
            );
        }

        public XsAttributeGroup CreateAttributeGroup() {
            throw new NotImplementedException();
        }
    }
    public sealed class XsRootElementFactory {
        private readonly XsSchema m_schema;
        private readonly string m_name;

        internal XsRootElementFactory(
            XsSchema schema,
            string name) {

            m_schema = schema;
            m_name = name;
        }

        public XsLoader Loader {
            get { return Schema.Loader; }
        }
        public XsSchema Schema {
            get { return m_schema; }
        }
        public string Name {
            get { return m_name; }
        }

        public XsElement CreateRootElement(
            XsType type,
            XsMetadataFactory metadata = null
        ) {

            return new XsElement(
                parent: Schema,
                name: Name,

                type: type,

                @default: null,
                form: XsForm.Qualified,
                maxOccurs: 1,
                minOccurs: 0,

                metadata: metadata
            );
        }
    }
    public sealed class XsComplexContentFactory {
        private readonly object m_parent;

        internal XsComplexContentFactory(object parent) {
            m_parent = parent;
        }

        public XsLoader Loader {
            get { return Schema.Loader; }
        }
        public XsSchema Schema {
            get { return XsComplexContent.GetDeclaringSchema(m_parent); }
        }
        public XsType DeclaringType {
            get { return XsComplexContent.GetDeclaringType(m_parent); }
        }
        public XsComplexContent ContentParent {
            get { return m_parent as XsComplexContent; }
        }

        public XsElement CreateElement(
            string name,
            XsType type,

            XsForm form = XsForm.Undefined,
            object @default = null,
            int minOccurs = 1,
            int maxOccurs = 1,
            XsMetadataFactory metadata = null
        ) {

            return new XsElement(
                parent: m_parent,

                name: name,
                form: form,
                type: type,
                @default: @default,
                minOccurs: minOccurs,
                maxOccurs: maxOccurs,

                metadata: metadata
            );
        }

        public XsSequence CreateSequence(
            Func<XsComplexContentFactory, IEnumerable<XsComplexContent>> contentFactory,
            int minOccurs = 1,
            int maxOccurs = 1,
            XsMetadataFactory metadata = null
        ) {

            return new XsSequence(
                parent: m_parent,

                contentFactory: contentFactory,
                minOccurs: minOccurs,
                maxOccurs: maxOccurs,

                metadata: metadata
            );
        }

        public XsChoice CreateChoice(
            Func<XsComplexContentFactory, IEnumerable<XsComplexContent>> contentFactory,
            int minOccurs = 1,
            int maxOccurs = 1,
            XsMetadataFactory metadata = null
        ) {

            return new XsChoice(
                parent: m_parent,

                minOccurs: minOccurs,
                maxOccurs: maxOccurs,
                contentFactory: contentFactory,

                metadata: metadata
            );
        }

        public XsAny CreateAny(
            int minOccurs = 1,
            int maxOccurs = 1,
            XsProcessContents processContents = XsProcessContents.Strict,
            IEnumerable<XNamespace> namespaces = null,
            XsNamespaceType type = XsNamespaceType.None,
            XsMetadataFactory metadata = null
        ) {

            return new XsAny(
                parent: m_parent,

                minOccurs: minOccurs,
                maxOccurs: maxOccurs,
                processContents: processContents,
                namespaces: namespaces,
                type: type,

                metadata: metadata
            );
        }

        public XsAll CreateAll(
            Func<XsComplexContentFactory, IEnumerable<XsElement>> contentFactory,
            XsMetadataFactory metadata = null
        ) {

            return new XsAll(
                parent: m_parent,
                contentFactory: contentFactory,

                metadata: metadata
            );
        }
    }
    public sealed class XsFacetFactory {
        private XsType m_type;

        internal XsFacetFactory(XsType type) {
            m_type = type;
        }

        public XsLoader Loader {
            get { return Schema.Loader; }
        }
        public XsSchema Schema {
            get { return Type.Schema; }
        }
        public XsType Type {
            get { return m_type; }
        }

        public XsFacet CreateEnumerationFacet(IEnumerable<object> values) {
            return new XsFacet.Enumeration(Type, values);
        }
        public XsFacet CreatePatternFacet(params string[] patterns) {
            return new XsFacet.Pattern(Type, patterns);
        }

        public XsFacet CreateCardinalityFacet(
            object cardinality, 
            bool isFixed = false) {
            return new XsFacet.Cardinality(Type, cardinality, isFixed);
        }
        public XsFacet CreateMaximumCardinalityFacet(
            object maximumCardinality,
            bool isFixed = false) {
            return new XsFacet.MaximumCardinality(Type, maximumCardinality, isFixed);
        }
        public XsFacet CreateMinimumCardinalityFacet(
            object minimumCardinality,
            bool isFixed = false) {
            return new XsFacet.MinimumCardinality(Type, minimumCardinality, isFixed);
        }

        public XsFacet CreateLengthFacet(
            object length,
            bool isFixed = false) {
            return new XsFacet.Length(Type, length, isFixed);
        }
        public XsFacet CreateMaximumLengthFacet(
            object maximumLength,
            bool isFixed = false) {
            return new XsFacet.MaximumLength(Type, maximumLength, isFixed);
        }
        public XsFacet CreateMinimumLengthFacet(
            object minimumLength,
            bool isFixed = false) {
            return new XsFacet.MinimumLength(Type, minimumLength, isFixed);
        }

        public XsFacet CreateMinimumInclusiveFacet(
            object minimumInclusive,
            bool isFixed = false) {
            return new XsFacet.MinimumInclusive(Type, minimumInclusive, isFixed);
        }
        public XsFacet CreateMaximumInclusiveFacet(
            object maximumInclusive,
            bool isFixed = false) {
            return new XsFacet.MaximumInclusive(Type, maximumInclusive, isFixed);
        }
        public XsFacet CreateMinimumExclusiveFacet(
            object minimumExclusive,
            bool isFixed = false) {
            return new XsFacet.MinimumExclusive(Type, minimumExclusive, isFixed);
        }
        public XsFacet CreateMaximumExclusiveFacet(
            object maximumExclusive,
            bool isFixed = false) {
            return new XsFacet.MaximumExclusive(Type, maximumExclusive, isFixed);
        }

        public XsFacet CreateTotalDigitsFacet(
            object totalDigits,
            bool isFixed = false) {
            return new XsFacet.TotalDigits(Type, totalDigits, isFixed);
        }
        public XsFacet CreateFractionDigitsFacet(
            object fractionDigits,
            bool isFixed = false) {
            return new XsFacet.FractionDigits(Type, fractionDigits, isFixed);
        }
        public XsFacet CreateWhitespaceFacet(
            XsWhitespaceFacetType whiteSpace,
            bool isFixed = false) {
            return new XsFacet.Whitespace(Type, whiteSpace, isFixed);
        }
        public XsFacet CreateExplicitTimeZoneFacet(
            XsExplicitTimezoneFacetType explicitTimeZone,
            bool isFixed = false) {
            return new XsFacet.ExplicitTimeZone(Type, explicitTimeZone, isFixed);
        }
    }

    public sealed class XsMetadataFactory {
        internal static XsMetadataFactory Empty = new XsMetadataFactory();

        private readonly Func<XsObject, XElement> m_appInfo;
        private readonly Func<XsObject, XElement> m_documentation;
        private readonly Func<XsObject, string> m_comment;

        public XsMetadataFactory(
            Func<XsObject, XElement> appInfo = null,
            Func<XsObject, XElement> documentation = null,
            Func<XsObject, string> comment = null) {

            m_appInfo = appInfo ?? (o => null);
            m_documentation = documentation ?? (o => null);
            m_comment = comment ?? (o => null);
        }

        internal Lazy<XElement> GetAppInfo(XsObject content) {
            return new Lazy<XElement>(() => m_appInfo(content));
        }
        internal Lazy<XElement> GetDocumentation(XsObject content) {
            return new Lazy<XElement>(() => m_documentation(content));
        }
        internal Lazy<string> GetComment(XsObject content) {
            return new Lazy<string>(() => m_comment(content));
        }
    }
}
